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 );

Reply via email to