sw/inc/crsrsh.hxx | 5 +- sw/inc/formatcontentcontrol.hxx | 1 sw/qa/uibase/wrtsh/wrtsh.cxx | 39 ++++++++++++++++++ sw/source/core/crsr/crstrvl.cxx | 54 ++++++++++++++++++++++++++ sw/source/core/txtnode/attrcontentcontrol.cxx | 4 + sw/source/core/txtnode/txatbase.cxx | 2 sw/source/uibase/docvw/edtwin.cxx | 13 +++++- sw/source/uibase/inc/wrtsh.hxx | 3 + sw/source/uibase/wrtsh/wrtsh3.cxx | 20 +++++++++ 9 files changed, 139 insertions(+), 2 deletions(-)
New commits: commit 07a1442b2441cc032c05fd9abf5b1a8db6c7e007 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu Apr 7 09:11:09 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Apr 7 10:57:04 2022 +0200 sw content controls: select the content on click when showing placeholder - teach SwCursorShell::GetContentAtPos() about a new IsAttrAtPos::ContentControl - add a new SwCursorShell::GotoFormatContentControl() to select a content control, and a SwWrtShell::GotoContentControl() wrapper around it - combine these together in SwEditWin::MouseButtonUp() The intention is that when you open a document and you click on a placeholder text like "Click here to enter text", then this content is pre-selected (so typing overwrites it), but typing real content there disables this behavior. Change-Id: Ia539865da7b18c41cbfb398282842bdb2e25f0bc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132652 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 6d843e7e424d..a34e02d45ead 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -89,9 +89,10 @@ enum class IsAttrAtPos ,CurrAttrs = 0x4000 ///< only for debugging ,TableBoxValue = 0x8000 ///< only for debugging #endif + , ContentControl = 0x10000 }; namespace o3tl { - template<> struct typed_flags<IsAttrAtPos> : is_typed_flags<IsAttrAtPos, 0xffff> {}; + template<> struct typed_flags<IsAttrAtPos> : is_typed_flags<IsAttrAtPos, 0x1ffff> {}; } struct SwContentAtPos @@ -703,6 +704,8 @@ public: bool GotoFormatField( const SwFormatField& rField ); + bool GotoFormatContentControl(const SwFormatContentControl& rContentControl); + static SwTextField* GetTextFieldAtPos( const SwPosition* pPos, const bool bIncludeInputFieldAtStart ); diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx index 097f4ae7c179..ba29a6825523 100644 --- a/sw/inc/formatcontentcontrol.hxx +++ b/sw/inc/formatcontentcontrol.hxx @@ -62,6 +62,7 @@ public: void NotifyChangeTextNode(SwTextNode* pTextNode); static SwFormatContentControl* CreatePoolDefault(sal_uInt16 nWhich); SwContentControl* GetContentControl() { return m_pContentControl.get(); } + const SwContentControl* GetContentControl() const { return m_pContentControl.get(); } void dumpAsXml(xmlTextWriterPtr pWriter) const override; }; diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx index 9f63ed2fc7e6..d73ba55ef09f 100644 --- a/sw/qa/uibase/wrtsh/wrtsh.cxx +++ b/sw/qa/uibase/wrtsh/wrtsh.cxx @@ -13,6 +13,7 @@ #include <com/sun/star/text/XTextContent.hpp> #include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/XTextDocument.hpp> #include <rtl/ustring.hxx> #include <sal/types.h> @@ -21,6 +22,8 @@ #include <docsh.hxx> #include <formatlinebreak.hxx> #include <wrtsh.hxx> +#include <ndtxt.hxx> +#include <textcontentcontrol.hxx> namespace { @@ -51,6 +54,42 @@ CPPUNIT_TEST_FIXTURE(Test, testInsertLineBreak) auto eClear = getProperty<sal_Int16>(xLineBreak, "Clear"); CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(SwLineBreakClear::ALL), eClear); } + +CPPUNIT_TEST_FIXTURE(Test, testGotoContentControl) +{ + // Given a document with a content control: + SwDoc* pDoc = createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("ShowingPlaceHolder", uno::makeAny(true)); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When going to that content control in placeholder mode: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwNodeOffset nIndex = pWrtShell->GetCursor()->GetNode().GetIndex(); + SwTextNode* pTextNode = pDoc->GetNodes()[nIndex]->GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + pWrtShell->GotoContentControl(rFormatContentControl); + + // Then make sure that the content control is selected (without the dummy character): + // Without the accompanying fix in place, this test would have failed, the user had to manually + // select the placeholder text. + sal_Int32 nStart = pWrtShell->GetCursor()->Start()->nContent.GetIndex(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), nStart); + sal_Int32 nEnd = pWrtShell->GetCursor()->End()->nContent.GetIndex(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(5), nEnd); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index f1a0d4e742ac..b5720002e916 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -74,6 +74,7 @@ #include <frameformats.hxx> #include <docsh.hxx> #include <wrtsh.hxx> +#include <textcontentcontrol.hxx> using namespace ::com::sun::star; @@ -852,6 +853,47 @@ bool SwCursorShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote) return bRet; } +bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rContentControl) +{ + bool bRet = false; + auto pContentControl = const_cast<SwContentControl*>(rContentControl.GetContentControl()); + if (!pContentControl->GetShowingPlaceHolder()) + { + return bRet; + } + + const SwTextContentControl* pTextContentControl = pContentControl->GetTextAttr(); + if (pTextContentControl) + { + CurrShell aCurr(this); + SwCallLink aLink(*this); + + SwCursor* pCursor = getShellCursor(true); + SwCursorSaveState aSaveState(*pCursor); + + pCursor->SetMark(); + SwTextNode* pTextNode = pContentControl->GetTextNode(); + pCursor->GetPoint()->nNode = *pTextNode; + // Don't select the text attribute itself at the start. + sal_Int32 nStart = pTextContentControl->GetStart() + 1; + pCursor->GetPoint()->nContent.Assign(pTextNode, nStart); + pCursor->GetMark()->nNode = *pTextNode; + pCursor->GetMark()->nContent.Assign(pTextNode, *(pTextContentControl->End())); + + // Assume that once the placeholder is selected, the content is no longer the placeholder. + pContentControl->SetShowingPlaceHolder(false); + + bRet = !pCursor->IsSelOvr(); + if (bRet) + { + UpdateCursor(SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE + | SwCursorShell::READONLY); + } + } + + return bRet; +} + bool SwCursorShell::GotoFormatField( const SwFormatField& rField ) { bool bRet = false; @@ -1464,6 +1506,18 @@ bool SwCursorShell::GetContentAtPos( const Point& rPt, } } + if (!bRet && rContentAtPos.eContentAtPos & IsAttrAtPos::ContentControl) + { + SwTextAttr* pAttr = pTextNd->GetTextAttrAt( + aPos.nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT); + if (pAttr) + { + rContentAtPos.eContentAtPos = IsAttrAtPos::ContentControl; + rContentAtPos.pFndTextAttr = pAttr; + bRet = true; + } + } + if( !bRet && IsAttrAtPos::Ftn & rContentAtPos.eContentAtPos ) { if( aTmpState.m_bFootnoteNoInfo ) diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 01af0eaec704..066ddd0494be 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -241,6 +241,10 @@ SwTextContentControl::SwTextContentControl(SwFormatContentControl& rAttr, sal_In { rAttr.SetTextAttr(this); SetHasDummyChar(true); + + // If the user types at the end of the content control, expand the text attr to the right. + SetLockExpandFlag(false); + SetDontExpand(false); } SwTextContentControl::~SwTextContentControl() diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx index a6fe1599519b..c846084a312e 100644 --- a/sw/source/core/txtnode/txatbase.cxx +++ b/sw/source/core/txtnode/txatbase.cxx @@ -94,6 +94,8 @@ void SwTextAttr::dumpAsXml(xmlTextWriterPtr pWriter) const BAD_CAST(typeid(*this).name())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("start"), BAD_CAST(OString::number(m_nStart).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("dont-expand"), + BAD_CAST(OString::boolean(m_bDontExpand).getStr())); if (End()) (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("end"), BAD_CAST(OString::number(*End()).getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr())); diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index feaacb1bdfea..594c69e3ec87 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -150,6 +150,7 @@ #include <cntfrm.hxx> #include <txtfrm.hxx> #include <strings.hrc> +#include <textcontentcontrol.hxx> using namespace sw::mark; using namespace ::com::sun::star; @@ -4737,7 +4738,8 @@ void SwEditWin::MouseButtonUp(const MouseEvent& rMEvt) SwContentAtPos aContentAtPos( IsAttrAtPos::Field | IsAttrAtPos::InetAttr | - IsAttrAtPos::SmartTag | IsAttrAtPos::FormControl); + IsAttrAtPos::SmartTag | IsAttrAtPos::FormControl | + IsAttrAtPos::ContentControl); if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) ) { @@ -4792,6 +4794,15 @@ void SwEditWin::MouseButtonUp(const MouseEvent& rMEvt) rSh.LeaveAddMode(); } } + else if (aContentAtPos.eContentAtPos == IsAttrAtPos::ContentControl) + { + auto pTextContentControl + = static_txtattr_cast<const SwTextContentControl*>( + aContentAtPos.pFndTextAttr); + const SwFormatContentControl& rFormatContentControl + = pTextContentControl->GetContentControl(); + rSh.GotoContentControl(rFormatContentControl); + } else if ( IsAttrAtPos::SmartTag == aContentAtPos.eContentAtPos ) { // execute smarttag menu diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx index 1c85873bc249..e271569f0bf8 100644 --- a/sw/source/uibase/inc/wrtsh.hxx +++ b/sw/source/uibase/inc/wrtsh.hxx @@ -54,6 +54,7 @@ enum class SvMacroItemId : sal_uInt16; class SwFieldMgr; class SfxRequest; enum class SwLineBreakClear; +class SwContentControl; namespace i18nutil { struct SearchOptions2; @@ -424,6 +425,8 @@ typedef bool (SwWrtShell::*FNSimpleMove)(); bool GotoField( const SwFormatField& rField ); + bool GotoContentControl(const SwFormatContentControl& rContentControl); + // jump to the next / previous hyperlink - inside text and also // on graphics void SelectNextPrevHyperlink( bool bNext ); diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx b/sw/source/uibase/wrtsh/wrtsh3.cxx index 7d30c3780fc2..2b0b9385467e 100644 --- a/sw/source/uibase/wrtsh/wrtsh3.cxx +++ b/sw/source/uibase/wrtsh/wrtsh3.cxx @@ -83,6 +83,26 @@ bool SwWrtShell::GotoField( const SwFormatField& rField ) return bRet; } +bool SwWrtShell::GotoContentControl(const SwFormatContentControl& rContentControl) +{ + (this->*m_fnKillSel)(nullptr, false); + + bool bRet = SwCursorShell::GotoFormatContentControl(rContentControl); + if (bRet && IsSelFrameMode()) + { + UnSelectFrame(); + LeaveSelFrameMode(); + } + + if (IsSelection()) + { + m_fnKillSel = &SwWrtShell::ResetSelect; + m_fnSetCursor = &SwWrtShell::SetCursorKillSel; + } + + return bRet; +} + bool SwWrtShell::GotoFieldmark(::sw::mark::IFieldmark const * const pMark) { (this->*m_fnKillSel)( nullptr, false );