sw/inc/viscrs.hxx                 |    5 ++
 sw/qa/core/unocore/unocore.cxx    |   12 +++++
 sw/source/core/crsr/crsrsh.cxx    |    2 
 sw/source/core/crsr/viscrs.cxx    |   78 ++++++++++++++++++++++++++++++++++++++
 sw/source/core/inc/swfont.hxx     |    3 +
 sw/source/core/inc/txttypes.hxx   |    1 
 sw/source/core/text/atrhndl.hxx   |    2 
 sw/source/core/text/atrstck.cxx   |   15 +++++++
 sw/source/core/text/inftxt.cxx    |    1 
 sw/source/core/text/itratr.cxx    |    2 
 sw/source/core/text/itrform2.cxx  |   20 +++++++++
 sw/source/core/text/xmldump.cxx   |    2 
 sw/source/core/txtnode/ndtxt.cxx  |    1 
 sw/source/core/txtnode/swfont.cxx |    3 +
 14 files changed, 146 insertions(+), 1 deletion(-)

New commits:
commit 8fdb2baf5400f7225aa78d950fd53071950877e8
Author:     Miklos Vajna <[email protected]>
AuthorDate: Wed Apr 6 08:40:31 2022 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Fri Apr 22 08:31:31 2022 +0200

    sw content controls: add overlay to render a border around the text portions
    
    - introduce SwSelPaintRects::HighlightContentControl() to create the
      overlay
    
    - use GetTextAttrAt() to enable this when we're inside a content control
      (not only when the cursor is exactly at the dummy char of the content
      control, which is at the start)
    
    - tested that this works correctly even if the portions inside the
      content control don't have the same height (similar to the selection
      overlay)
    
    (cherry picked from commit c2999f9f3e0efa04f89055399f2d176c9c95e92d)
    
    Change-Id: I1db51cdb1ddc87c45740cd8677de645dc8906697
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133244
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx
index 6755b548e8a0..2eb2f787fbc7 100644
--- a/sw/inc/viscrs.hxx
+++ b/sw/inc/viscrs.hxx
@@ -84,8 +84,11 @@ class SwSelPaintRects : public SwRects
 
     bool m_bShowTextInputFieldOverlay;
     std::unique_ptr<sw::overlay::OverlayRangesOutline> 
m_pTextInputFieldOverlay;
+    bool m_bShowContentControlOverlay;
+    std::unique_ptr<sw::overlay::OverlayRangesOutline> 
m_pContentControlOverlay;
 
     void HighlightInputField();
+    void HighlightContentControl();
 
 public:
     SwSelPaintRects( const SwCursorShell& rCSh );
@@ -108,6 +111,8 @@ public:
         m_bShowTextInputFieldOverlay = bShow;
     }
 
+    void SetShowContentControlOverlay(const bool bShow) { 
m_bShowContentControlOverlay = bShow; }
+
     const SwCursorShell* GetShell() const { return m_pCursorShell; }
     // check current MapMode of the shell and set possibly the static members.
     // Optional set the parameters pX, pY
diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx
index ae7b2c81133d..4e9f54aab2e8 100644
--- a/sw/source/core/crsr/crsrsh.cxx
+++ b/sw/source/core/crsr/crsrsh.cxx
@@ -2393,6 +2393,7 @@ void SwCursorShell::ShowCursor()
 
     m_bSVCursorVis = true;
     m_pCurrentCursor->SetShowTextInputFieldOverlay( true );
+    m_pCurrentCursor->SetShowContentControlOverlay(true);
 
     if (comphelper::LibreOfficeKit::isActive())
     {
@@ -2413,6 +2414,7 @@ void SwCursorShell::HideCursor()
     // possibly reverse selected areas!!
     CurrShell aCurr( this );
     m_pCurrentCursor->SetShowTextInputFieldOverlay( false );
+    m_pCurrentCursor->SetShowContentControlOverlay(false);
     m_pVisibleCursor->Hide();
 
     if (comphelper::LibreOfficeKit::isActive())
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 0501162a2a16..d72289bdbb36 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -59,6 +59,7 @@
 #include <svtools/optionsdrawinglayer.hxx>
 #include <cellfrm.hxx>
 #include <wrtsh.hxx>
+#include <textcontentcontrol.hxx>
 
 // Here static members are defined. They will get changed on alteration of the
 // MapMode. This is done so that on ShowCursor the same size does not have to 
be
@@ -350,6 +351,7 @@ SwSelPaintRects::SwSelPaintRects( const SwCursorShell& rCSh 
)
     : m_pCursorShell( &rCSh )
 #if HAVE_FEATURE_DESKTOP
     , m_bShowTextInputFieldOverlay(true)
+    , m_bShowContentControlOverlay(true)
 #endif
 {
 }
@@ -368,6 +370,8 @@ void SwSelPaintRects::swapContent(SwSelPaintRects& rSwap)
     std::swap(m_pCursorOverlay, rSwap.m_pCursorOverlay);
     std::swap(m_bShowTextInputFieldOverlay, 
rSwap.m_bShowTextInputFieldOverlay);
     std::swap(m_pTextInputFieldOverlay, rSwap.m_pTextInputFieldOverlay);
+    std::swap(m_bShowContentControlOverlay, 
rSwap.m_bShowContentControlOverlay);
+    std::swap(m_pContentControlOverlay, rSwap.m_pContentControlOverlay);
 #endif
 }
 
@@ -376,6 +380,7 @@ void SwSelPaintRects::Hide()
 #if HAVE_FEATURE_DESKTOP
     m_pCursorOverlay.reset();
     m_pTextInputFieldOverlay.reset();
+    m_pContentControlOverlay.reset();
 #endif
 
     SwRects::clear();
@@ -466,6 +471,7 @@ void SwSelPaintRects::Show(std::vector<OString>* 
pSelectionRectangles)
     }
 
     HighlightInputField();
+    HighlightContentControl();
 #endif
 
     // Tiled editing does not expose the draw and writer cursor, it just
@@ -625,6 +631,78 @@ void SwSelPaintRects::HighlightInputField()
     }
 }
 
+void SwSelPaintRects::HighlightContentControl()
+{
+    std::vector<basegfx::B2DRange> aContentControlRanges;
+
+    if (m_bShowContentControlOverlay)
+    {
+        const SwPosition* pStart = GetShell()->GetCursor()->Start();
+        SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+        SwTextContentControl* pCurContentControlAtCursor = nullptr;
+        if (pTextNode)
+        {
+            // SwTextNode::PARENT because this way we highlight when the 
cursor is on the right side
+            // of the dummy character: ideally the end of the range would have 
the same behavior.
+            SwTextAttr* pAttr = pTextNode->GetTextAttrAt(
+                pStart->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, 
SwTextNode::PARENT);
+            if (pAttr)
+            {
+                pCurContentControlAtCursor = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+            }
+        }
+        if (pCurContentControlAtCursor)
+        {
+            auto pCursorForContentControl = std::make_unique<SwShellCursor>(
+                *GetShell(), SwPosition(*pTextNode, 
pCurContentControlAtCursor->GetStart()));
+            pCursorForContentControl->SetMark();
+            pCursorForContentControl->GetMark()->nNode = *pTextNode;
+            pCursorForContentControl->GetMark()->nContent.Assign(
+                pTextNode, *(pCurContentControlAtCursor->End()));
+
+            pCursorForContentControl->FillRects();
+            SwRects* pRects = pCursorForContentControl.get();
+            for (const auto& rRect : *pRects)
+            {
+                tools::Rectangle aRect(rRect.SVRect());
+
+                aContentControlRanges.emplace_back(aRect.Left(), aRect.Top(), 
aRect.Right() + 1,
+                                                   aRect.Bottom() + 1);
+            }
+        }
+    }
+
+    if (!aContentControlRanges.empty())
+    {
+        if (m_pContentControlOverlay)
+        {
+            
m_pContentControlOverlay->setRanges(std::move(aContentControlRanges));
+        }
+        else
+        {
+            SdrView* pView = const_cast<SdrView*>(GetShell()->GetDrawView());
+            SdrPaintWindow* pCandidate = pView->GetPaintWindow(0);
+            const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay
+                = pCandidate->GetOverlayManager();
+
+            if (xTargetOverlay.is())
+            {
+                // Use the system's highlight color with decreased luminance 
as highlight color.
+                Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+                aHighlight.DecreaseLuminance(128);
+
+                m_pContentControlOverlay.reset(new 
sw::overlay::OverlayRangesOutline(
+                    aHighlight, std::move(aContentControlRanges)));
+                xTargetOverlay->add(*m_pContentControlOverlay);
+            }
+        }
+    }
+    else
+    {
+        m_pContentControlOverlay.reset();
+    }
+}
+
 void SwSelPaintRects::Invalidate( const SwRect& rRect )
 {
     size_type nSz = size();
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 4d109a8add1c..e5040c50a1a7 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -1715,6 +1715,7 @@ SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, 
sal_uInt16 const nWhich,
             || (nWhich == RES_TXTATR_INETFMT)
             || (nWhich == RES_TXTATR_CJK_RUBY)
             || (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
+            || (nWhich == RES_TXTATR_CONTENTCONTROL)
             || (nWhich == RES_TXTATR_INPUTFIELD ) );
             // "GetTextAttrAt() will give wrong result for this hint!")
 
commit b6f3fc87699c16db2ccc73f992bbcccfb60695bf
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Apr 5 10:53:22 2022 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Fri Apr 22 08:31:17 2022 +0200

    sw content controls: add initial layout support
    
    - portions inside content controls are not text portions but content
      control portions
    
    - teach SwTextPaintInfo::DrawViewOpt() to paint field shadings for
      content control portions
    
    - teach the attribute stack code about RES_TXTATR_CONTENTCONTROL, so if
      the whole document is just a content control, then adding text
      before/after the content control is properly text portions, not content
      control portions
    
    (cherry picked from commit 8c632d8a837cc722c6e7b3b400f6d97edf9f9800)
    
    Change-Id: Ia9f955a5f7c7a4fd633899fafa8fc723e7c0d050
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133243
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 4f9479a673d1..8b2f3eb5f6ec 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -351,6 +351,18 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, 
testContentControlTextPortionEnum)
     uno::Reference<container::XEnumeration> xContentEnum = 
xContentEnumAccess->createEnumeration();
     uno::Reference<text::XTextRange> xContent(xContentEnum->nextElement(), 
uno::UNO_QUERY);
     CPPUNIT_ASSERT_EQUAL(OUString("test"), xContent->getString());
+
+    // Also test the generated layout:
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[1]", 
"type",
+                "PortionType::Field");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: PortionType::ContentControl
+    // - Actual  : PortionType::Text
+    // i.e. SwContentControl generated a plain text portion, not a dedicated 
content control
+    // portion.
+    assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[2]", 
"type",
+                "PortionType::ContentControl");
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/inc/swfont.hxx b/sw/source/core/inc/swfont.hxx
index a2eedaca5f64..40a4c2e8d66c 100644
--- a/sw/source/core/inc/swfont.hxx
+++ b/sw/source/core/inc/swfont.hxx
@@ -158,6 +158,7 @@ class SwFont
     sal_uInt8   m_nToxCount;        // counts the nesting depth of the Tox
     sal_uInt8   m_nRefCount;        // counts the nesting depth of the Refs
     sal_uInt8   m_nMetaCount;   // count META/METAFIELD
+    sal_uInt8 m_nContentControlCount; // count CONTENTCONTROL
     sal_uInt8   m_nInputFieldCount; // count INPUTFIELD
 
     SwFontScript m_nActual;        // actual font (Latin, CJK or CTL)
@@ -250,6 +251,8 @@ public:
     bool IsRef() const { return ( 0 != m_nRefCount ); }
     sal_uInt8 &GetMeta() { return m_nMetaCount; }
     bool IsMeta() const { return (0 != m_nMetaCount); }
+    sal_uInt8& GetContentControl() { return m_nContentControlCount; }
+    bool IsContentControl() const { return m_nContentControlCount != 0; }
     sal_uInt8 &GetInputField() { return m_nInputFieldCount; }
     bool IsInputField() const { return (0 != m_nInputFieldCount); }
     inline void SetGreyWave( const bool bNew );
diff --git a/sw/source/core/inc/txttypes.hxx b/sw/source/core/inc/txttypes.hxx
index e65ed26dc128..d876b0f8e037 100644
--- a/sw/source/core/inc/txttypes.hxx
+++ b/sw/source/core/inc/txttypes.hxx
@@ -49,6 +49,7 @@ enum class PortionType
     Ref         = 0x808b,
     IsoRef      = 0x808c,
     Meta        = 0x808d,
+    ContentControl = 0x808e,
 
     Expand      = 0xc080,
     Blank       = 0xc081,
diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx
index 6c719bf2cbaf..e2d5ed6ea523 100644
--- a/sw/source/core/text/atrhndl.hxx
+++ b/sw/source/core/text/atrhndl.hxx
@@ -18,7 +18,7 @@
  */
 
 #pragma once
-#define NUM_ATTRIBUTE_STACKS 44
+#define NUM_ATTRIBUTE_STACKS 45
 
 #include <memory>
 #include <vector>
diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx
index 30a88c30110d..1d62f1cc1e5f 100644
--- a/sw/source/core/text/atrstck.cxx
+++ b/sw/source/core/text/atrstck.cxx
@@ -123,6 +123,7 @@ const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - 
RES_CHRATR_BEGIN + 1 ] =
     42, // RES_TXTATR_CJK_RUBY,                  // 53
      0, // RES_TXTATR_UNKNOWN_CONTAINER,         // 54
     43, // RES_TXTATR_INPUTFIELD                 // 55
+    44, // RES_TXTATR_CONTENTCONTROL             // 56
 };
 
 namespace CharFormat
@@ -517,6 +518,10 @@ void SwAttrHandler::ActivateTop( SwFont& rFnt, const 
sal_uInt16 nAttr )
     {
         rFnt.GetMeta()--;
     }
+    else if (nAttr == RES_TXTATR_CONTENTCONTROL)
+    {
+        rFnt.GetContentControl()--;
+    }
     else if ( RES_TXTATR_CJK_RUBY == nAttr )
     {
         // ruby stack has no more attributes
@@ -811,6 +816,16 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, 
SwFont& rFnt, bool bPush )
             else
                 rFnt.GetMeta()--;
             break;
+        case RES_TXTATR_CONTENTCONTROL:
+            if (bPush)
+            {
+                rFnt.GetContentControl()++;
+            }
+            else
+            {
+                rFnt.GetContentControl()--;
+            }
+            break;
         case RES_TXTATR_INPUTFIELD :
             if ( bPush )
                 rFnt.GetInputField()++;
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
index 422187624cd9..cd248307597f 100644
--- a/sw/source/core/text/inftxt.cxx
+++ b/sw/source/core/text/inftxt.cxx
@@ -1319,6 +1319,7 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion 
&rPor,
     case PortionType::Tox:
     case PortionType::Ref:
     case PortionType::Meta:
+    case PortionType::ContentControl:
     case PortionType::ControlChar:
         if ( !GetOpt().IsPagePreview()
              && !GetOpt().IsReadonly()
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index 95f7929a0e14..4f4a840a564f 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -510,6 +510,7 @@ static bool CanSkipOverRedline(
                 case RES_TXTATR_INETFMT:
                 case RES_TXTATR_CJK_RUBY:
                 case RES_TXTATR_INPUTFIELD:
+                case RES_TXTATR_CONTENTCONTROL:
                     {
                         if (!isTheAnswerYes) return false; // always break
                     }
@@ -600,6 +601,7 @@ static bool CanSkipOverRedline(
                 case RES_TXTATR_INETFMT:
                 case RES_TXTATR_CJK_RUBY:
                 case RES_TXTATR_INPUTFIELD:
+                case RES_TXTATR_CONTENTCONTROL:
                     {
                         if (!isTheAnswerYes) return false;
                     }
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 891678087eee..50fdf3f6acf5 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -863,6 +863,13 @@ public:
     void SetShadowColor(const Color& rCol ) { m_aShadowColor = rCol; }
 };
 
+/// A content control portion is a text portion that is inside 
RES_TXTATR_CONTENTCONTROL.
+class SwContentControlPortion : public SwTextPortion
+{
+public:
+    SwContentControlPortion() { SetWhichPor(PortionType::ContentControl); }
+    virtual void Paint(const SwTextPaintInfo& rInf) const override;
+};
 }
 
 void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) const
@@ -879,6 +886,15 @@ void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) 
const
     }
 }
 
+void SwContentControlPortion::Paint(const SwTextPaintInfo& rInf) const
+{
+    if (Width())
+    {
+        rInf.DrawViewOpt(*this, PortionType::ContentControl);
+        SwTextPortion::Paint(rInf);
+    }
+}
+
 namespace sw::mark {
     OUString ExpandFieldmark(IFieldmark* pBM)
     {
@@ -995,6 +1011,10 @@ SwTextPortion *SwTextFormatter::WhichTextPor( 
SwTextFormatInfo &rInf ) const
             }
             pPor = pMetaPor;
         }
+        else if (GetFnt()->IsContentControl())
+        {
+            pPor = new SwContentControlPortion;
+        }
         else
         {
             // Only at the End!
diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx
index 1e52e8fdbb68..5e1ac0eb002c 100644
--- a/sw/source/core/text/xmldump.cxx
+++ b/sw/source/core/text/xmldump.cxx
@@ -79,6 +79,8 @@ const char* sw::PortionTypeToString(PortionType nType)
             return "PortionType::IsoRef";
         case PortionType::Meta:
             return "PortionType::Meta";
+        case PortionType::ContentControl:
+            return "PortionType::ContentControl";
         case PortionType::FieldMark:
             return "PortionType::FieldMark";
         case PortionType::FieldFormCheckbox:
diff --git a/sw/source/core/txtnode/swfont.cxx 
b/sw/source/core/txtnode/swfont.cxx
index e0b64c24ef52..8553328e2da9 100644
--- a/sw/source/core/txtnode/swfont.cxx
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -707,6 +707,7 @@ SwFont::SwFont( const SwFont &rFont )
     m_nToxCount = 0;
     m_nRefCount = 0;
     m_nMetaCount = 0;
+    m_nContentControlCount = 0;
     m_nInputFieldCount = 0;
     m_bFontChg = rFont.m_bFontChg;
     m_bOrgChg = rFont.m_bOrgChg;
@@ -722,6 +723,7 @@ SwFont::SwFont( const SwAttrSet* pAttrSet,
     m_nToxCount = 0;
     m_nRefCount = 0;
     m_nMetaCount = 0;
+    m_nContentControlCount = 0;
     m_nInputFieldCount = 0;
     m_bPaintBlank = false;
     m_bGreyWave = false;
@@ -901,6 +903,7 @@ SwFont& SwFont::operator=( const SwFont &rFont )
         m_nToxCount = 0;
         m_nRefCount = 0;
         m_nMetaCount = 0;
+        m_nContentControlCount = 0;
         m_nInputFieldCount = 0;
         m_bFontChg = rFont.m_bFontChg;
         m_bOrgChg = rFont.m_bOrgChg;

Reply via email to