sw/inc/ToxTabStopTokenHandler.hxx             |   14 ++++-
 sw/inc/ToxTextGenerator.hxx                   |    3 -
 sw/qa/core/test_ToxTextGenerator.cxx          |    9 ++-
 sw/qa/extras/uiwriter/uiwriter6.cxx           |    2 
 sw/source/core/doc/doctxm.cxx                 |   45 ++++++++++++++----
 sw/source/core/tox/ToxTabStopTokenHandler.cxx |   64 +++++++++++++++++++-------
 sw/source/core/tox/ToxTextGenerator.cxx       |   14 ++---
 7 files changed, 111 insertions(+), 40 deletions(-)

New commits:
commit aa4f45f07331f02343b021f118e64252f3bf515e
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Wed Aug 7 11:35:42 2024 +0200
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Mon Aug 12 10:15:20 2024 +0200

    tdf#162121 sw: fix tab stop position in columned ToX
    
    There is a SvxTabAdjust::End but this only exists in the definition of
    index entry templates, it is not possible to set this on a tab stop on a
    text node.
    
    So the ToxTabStopTokenHandler converts this into a SvxTabAdjust::Right
    tab stop with a fixed position.
    
    DefaultToxTabStopTokenHandler::CanUseLayoutRectangle() has inverted
    condition but that doesn't matter because there are no layout frames at
    that point.
    (regression from commit 3aca57fb9c7c979d971cae6bb3ad73c6dc412685)
    
    The main problem is that getting the with from the layout rectangle
    never works because there's no layout frame, and the alternative only
    takes into account the page dimensions and not any margins of the
    section, columns, or gap between columns, not to mention that the ToX
    could also be in a table etc.
    
    Refactor this so that the tab stops are set only after all the layout
    frames are created.
    
    An unfixable problem remains in case the ToX has columns of different
    width.
    
    The wrong tab positions are problematic if TabOverMargin is enabled
    since commit 10d753b8aadb50ec4309551b97d4cf2163ea3e3d
    
    Change-Id: Ia712c9bf42b2518e396f1b9e7efd65869ebc5ab4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171585
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    Tested-by: Jenkins
    (cherry picked from commit 0633189fabe85f73062ff2ce67b5f40af7d3f504)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171596
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

diff --git a/sw/inc/ToxTabStopTokenHandler.hxx 
b/sw/inc/ToxTabStopTokenHandler.hxx
index 89a74fe6e65b..29d634c67914 100644
--- a/sw/inc/ToxTabStopTokenHandler.hxx
+++ b/sw/inc/ToxTabStopTokenHandler.hxx
@@ -45,8 +45,11 @@ public:
      *          and to provide the returned SvxTabStop to the attributes of 
the node.
      */
     virtual HandledTabStopToken
-    HandleTabStopToken(const SwFormToken& aToken, const SwTextNode& targetNode,
-            const SwRootFrame *currentLayout) const = 0;
+    HandleTabStopToken(const SwFormToken& aToken, const SwTextNode& targetNode)
+        const = 0;
+
+    virtual auto CalcEndStop(SwTextNode const& rNode,
+            SwRootFrame const* pLayout) const -> tools::Long = 0;
 };
 
 /** The default implementation of ToxTabStopTokenHandler */
@@ -83,8 +86,11 @@ public:
      * method behaves differently when deriving the tab stop position.
      */
     ToxTabStopTokenHandler::HandledTabStopToken
-    HandleTabStopToken(const SwFormToken& aToken, const SwTextNode& targetNode,
-            const SwRootFrame *currentLayout) const override;
+    HandleTabStopToken(const SwFormToken& aToken, const SwTextNode& targetNode)
+        const override;
+
+    auto CalcEndStop(SwTextNode const& rNode,
+            SwRootFrame const* pLayout) const -> tools::Long override;
 
 private:
     /** Test whether the page layout can be obtained by a layout rectangle.
diff --git a/sw/inc/ToxTextGenerator.hxx b/sw/inc/ToxTextGenerator.hxx
index 35eefd0fb7ab..105aa9eb0660 100644
--- a/sw/inc/ToxTextGenerator.hxx
+++ b/sw/inc/ToxTextGenerator.hxx
@@ -26,6 +26,7 @@
 #include "fmtautofmt.hxx"
 
 #include <memory>
+#include <optional>
 #include <vector>
 #include <unordered_map>
 
@@ -67,7 +68,7 @@ public:
      * This method will process the entries in @p entries, starting at @p 
indexOfEntryToProcess and
      * process @p numberOfEntriesToProcess entries.
      */
-    void
+    std::optional<std::pair<SwTextNode *, SvxTabStopItem>>
     GenerateText(SwDoc *doc,
         std::unordered_map<OUString, int> & rMarkURLs,
         const std::vector<std::unique_ptr<SwTOXSortTabBase>>& entries,
diff --git a/sw/qa/core/test_ToxTextGenerator.cxx 
b/sw/qa/core/test_ToxTextGenerator.cxx
index c0b667a27334..bc21e3de19e2 100644
--- a/sw/qa/core/test_ToxTextGenerator.cxx
+++ b/sw/qa/core/test_ToxTextGenerator.cxx
@@ -113,10 +113,15 @@ namespace {
 class MockedToxTabStopTokenHandler : public ToxTabStopTokenHandler {
 public:
     virtual HandledTabStopToken
-    HandleTabStopToken(const SwFormToken&, const SwTextNode&,
-            const SwRootFrame *) const override {
+    HandleTabStopToken(const SwFormToken&, const SwTextNode&) const override
+    {
         return HandledTabStopToken();
     }
+
+    auto CalcEndStop(SwTextNode const&, SwRootFrame const*) const -> 
tools::Long override
+    {
+        return 0;
+    }
 };
 
 class ToxTextGeneratorWithMockedChapterField : public ToxTextGenerator {
diff --git a/sw/qa/extras/uiwriter/uiwriter6.cxx 
b/sw/qa/extras/uiwriter/uiwriter6.cxx
index c9f4f5d5acd4..5a666501d3cb 100644
--- a/sw/qa/extras/uiwriter/uiwriter6.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter6.cxx
@@ -2194,7 +2194,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf116403)
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aTabs.getLength());
     // This was still 17000, refreshing ToX didn't take borders spacings and 
widths into account
     CPPUNIT_ASSERT_EQUAL_MESSAGE("Page borders must be considered for 
right-aligned tabstop",
-                                 static_cast<sal_Int32>(17000 - 2 * 500 - 2 * 
1),
+                                 static_cast<sal_Int32>(17000 - 2 * 500 - 2 * 
1 - 1),
                                  aTabs[0].Position);
 }
 
diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx
index f9533b37f647..a0ea88ec1936 100644
--- a/sw/source/core/doc/doctxm.cxx
+++ b/sw/source/core/doc/doctxm.cxx
@@ -1067,6 +1067,13 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
     // Sort the List of all TOC Marks and TOC Sections
     std::vector<SwTextFormatColl*> aCollArr( GetTOXForm().GetFormMax(), 
nullptr );
     std::unordered_map<OUString, int> markURLs;
+    std::vector<std::pair<SwTextNode *, SvxTabStopItem>> tabStops;
+    std::shared_ptr<sw::ToxTabStopTokenHandler> const pTabStopTokenHandler =
+        std::make_shared<sw::DefaultToxTabStopTokenHandler>(
+            pSectNd->GetIndex(), *pDefaultPageDesc, GetTOXForm().IsRelTabPos(),
+            
rDoc.GetDocumentSettingManager().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT)
+                ? 
sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_INDENT
+                : 
sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_PAGE);
     SwNodeIndex aInsPos( *pFirstEmptyNd, 1 );
     for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
     {
@@ -1081,7 +1088,6 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
             aCollArr[ nLvl ] = pColl;
         }
 
-        // Generate: Set dynamic TabStops
         SwTextNode* pTOXNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode() , 
pColl );
         m_aSortArr[ nCnt ]->pTOXNd = pTOXNd;
 
@@ -1112,14 +1118,13 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
         // to method <GenerateText(..)>.
         ::SetProgressState( 0, rDoc.GetDocShell() );
 
-        std::shared_ptr<sw::ToxTabStopTokenHandler> tabStopTokenHandler =
-                std::make_shared<sw::DefaultToxTabStopTokenHandler>(
-                        pSectNd->GetIndex(), *pDefaultPageDesc, 
GetTOXForm().IsRelTabPos(),
-                        
rDoc.GetDocumentSettingManager().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT)
 ?
-                                
sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_INDENT :
-                                
sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_PAGE);
-        sw::ToxTextGenerator ttgn(GetTOXForm(), tabStopTokenHandler);
-        ttgn.GenerateText(GetFormat()->GetDoc(), markURLs, m_aSortArr, nCnt, 
nRange, pLayout);
+        sw::ToxTextGenerator ttgn(GetTOXForm(), pTabStopTokenHandler);
+        std::optional<std::pair<SwTextNode *, SvxTabStopItem>> const oTabStops 
=
+            ttgn.GenerateText(GetFormat()->GetDoc(), markURLs, m_aSortArr, 
nCnt, nRange, pLayout);
+        if (oTabStops)
+        {
+            tabStops.emplace_back(*oTabStops);
+        }
         nCnt += nRange - 1;
     }
 
@@ -1157,6 +1162,28 @@ void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
     {
         SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(rpLayout->Lower()) 
);
     }
+    // delay setting tab stops until the layout frames exist, in case the ToX
+    // is in columns or other non-body environment; best way is to check uppers
+    // (what if columns have different widths? no idea what to do about 
that...)
+    for (auto & it : tabStops)
+    {
+        std::vector<SvxTabStop> tabs;
+        for (size_t i = 0; i < it.second.Count(); ++i)
+        {
+            tabs.emplace_back(it.second.At(i));
+        }
+        it.second.Remove(0, it.second.Count());
+        for (SvxTabStop & rTab : tabs)
+        {
+            if (rTab.GetAdjustment() == SvxTabAdjust::Right)
+            {
+                assert(rTab.GetTabPos() == 0);
+                rTab.GetTabPos() = 
pTabStopTokenHandler->CalcEndStop(*it.first, pLayout);
+            }
+            it.second.Insert(rTab);
+        }
+        it.first->SetAttr(it.second);
+    }
 
     SetProtect( SwTOXBase::IsProtected() );
 }
diff --git a/sw/source/core/tox/ToxTabStopTokenHandler.cxx 
b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
index b3943d6e5393..2cdcc358c971 100644
--- a/sw/source/core/tox/ToxTabStopTokenHandler.cxx
+++ b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
@@ -18,6 +18,7 @@
 #include <fmtpdsc.hxx>
 #include <frmfmt.hxx>
 #include <frmatr.hxx>
+#include <frmtool.hxx>
 #include <ndtxt.hxx>
 #include <pagedesc.hxx>
 #include <pagefrm.hxx>
@@ -40,7 +41,7 @@ 
DefaultToxTabStopTokenHandler::DefaultToxTabStopTokenHandler(SwNodeOffset indexO
 
 ToxTabStopTokenHandler::HandledTabStopToken
 DefaultToxTabStopTokenHandler::HandleTabStopToken(
-        const SwFormToken& aToken, const SwTextNode& targetNode, const 
SwRootFrame *currentLayout) const
+        const SwFormToken& aToken, const SwTextNode& targetNode) const
 {
     HandledTabStopToken result;
 
@@ -61,29 +62,62 @@ DefaultToxTabStopTokenHandler::HandleTabStopToken(
         return result;
     }
 
-    SwRect aNdRect;
-    if (CanUseLayoutRectangle(targetNode, currentLayout)) {
-        aNdRect = targetNode.FindLayoutRect(true);
-    }
+    // note: this will be filled later by CalcEndStop()
+    result.tabStop = SvxTabStop(0, SvxTabAdjust::Right, cDfltDecimalChar, 
aToken.cTabFillChar);
+    return result;
+}
+
+auto DefaultToxTabStopTokenHandler::CalcEndStop(SwTextNode const& rNode,
+        SwRootFrame const*const pLayout) const -> tools::Long
+{
     tools::Long nRightMargin;
-    if (aNdRect.IsEmpty()) {
-        nRightMargin = CalculatePageMarginFromPageDescription(targetNode);
-    } else {
-        nRightMargin = aNdRect.Width();
+    if (CanUseLayoutRectangle(rNode, pLayout))
+    {
+        // in case it's in a header, any frame should do
+        SwContentFrame const*const pFrame(rNode.getLayoutFrame(pLayout));
+        assert(pFrame); // created in SwTOXBaseSection::Update()
+        SwRectFnSet const fnRect(pFrame->GetUpper());
+        SwRect rect = pFrame->getFramePrintArea();
+        if (fnRect.GetWidth(rect) == 0) // typically it's newly created
+        {
+            if (pFrame->GetUpper()->IsSctFrame())
+            {   // this is set in SwSectionFrame::Init()
+                rect = pFrame->GetUpper()->GetUpper()->getFramePrintArea();
+                assert(fnRect.GetWidth(rect) != 0);
+            }
+            else if (pFrame->GetUpper()->IsColBodyFrame())
+            {
+                SwFrame const*const pColFrame(pFrame->GetUpper()->GetUpper());
+                assert(pColFrame->IsColumnFrame());
+                rect = pColFrame->getFrameArea();
+                // getFramePrintArea() is not valid yet, manually subtract...
+                // (it can have a border too!)
+                SwBorderAttrAccess access(SwFrame::GetCache(), pColFrame);
+                SwBorderAttrs const& rAttrs(*access.Get());
+                auto const nLeft(rAttrs.CalcLeft(pColFrame));
+                auto const nRight(rAttrs.CalcRight(pColFrame));
+                fnRect.SetWidth(rect, fnRect.GetWidth(rect) - nLeft - nRight);
+                assert(fnRect.GetWidth(rect) != 0);
+            }
+            else assert(false);
+        }
+        nRightMargin = fnRect.GetWidth(rect);
+    }
+    else
+    {
+        nRightMargin = CalculatePageMarginFromPageDescription(rNode);
     }
     //#i24363# tab stops relative to indent
     if (mTabStopReferencePolicy == TABSTOPS_RELATIVE_TO_INDENT) {
         // left margin of paragraph style
         SvxFirstLineIndentItem const& rFirstLine(
-            targetNode.GetTextColl()->GetFirstLineIndent());
+            rNode.GetTextColl()->GetFirstLineIndent());
         SvxTextLeftMarginItem const& rTextLeftMargin(
-            targetNode.GetTextColl()->GetTextLeftMargin());
+            rNode.GetTextColl()->GetTextLeftMargin());
         nRightMargin -= rTextLeftMargin.GetLeft(rFirstLine);
         nRightMargin -= rFirstLine.GetTextFirstLineOffset();
     }
-
-    result.tabStop = SvxTabStop(nRightMargin, SvxTabAdjust::Right, 
cDfltDecimalChar, aToken.cTabFillChar);
-    return result;
+    return nRightMargin - 1; // subtract 1 twip to avoid equal for 
TabOverMargin
 }
 
 tools::Long
@@ -113,7 +147,7 @@ DefaultToxTabStopTokenHandler::CanUseLayoutRectangle(const 
SwTextNode& targetNod
             targetNode.SwContentNode::GetAttr(RES_PAGEDESC).GetPageDesc();
 
     if (!pageDescription) {
-        return false;
+        return true;
     }
     const SwFrame* pFrame = targetNode.getLayoutFrame(currentLayout);
     if (!pFrame) {
diff --git a/sw/source/core/tox/ToxTextGenerator.cxx 
b/sw/source/core/tox/ToxTextGenerator.cxx
index 158cc0c72bc7..32d18d1c8087 100644
--- a/sw/source/core/tox/ToxTextGenerator.cxx
+++ b/sw/source/core/tox/ToxTextGenerator.cxx
@@ -165,15 +165,14 @@ ToxTextGenerator::GenerateTextForChapterToken(const 
SwFormToken& chapterToken, c
     return retval;
 }
 
-// Add parameter <_TOXSectNdIdx> and <_pDefaultPageDesc> in order to control,
-// which page description is used, no appropriate one is found.
-void
+std::optional<std::pair<SwTextNode *, SvxTabStopItem>>
 ToxTextGenerator::GenerateText(SwDoc* pDoc,
         std::unordered_map<OUString, int> & rMarkURLs,
         const std::vector<std::unique_ptr<SwTOXSortTabBase>> &entries,
         sal_uInt16 indexOfEntryToProcess, sal_uInt16 numberOfEntriesToProcess,
         SwRootFrame const*const pLayout)
 {
+    std::optional<std::pair<SwTextNode *, SvxTabStopItem>> oRet;
     // pTOXNd is only set at the first mark
     SwTextNode* pTOXNd = 
const_cast<SwTextNode*>(entries.at(indexOfEntryToProcess)->pTOXNd);
     // FIXME this operates directly on the node text
@@ -188,7 +187,7 @@ ToxTextGenerator::GenerateText(SwDoc* pDoc,
         sal_uInt16 nLvl = rBase.GetLevel();
         OSL_ENSURE( nLvl < mToxForm.GetFormMax(), "invalid FORM_LEVEL");
 
-        SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, 
RES_PARATR_TABSTOP );
+        oRet.emplace(pTOXNd, SvxTabStopItem(0, 0, SvxTabAdjust::Default, 
RES_PARATR_TABSTOP));
         // create an enumerator
         // #i21237#
         SwFormTokens aPattern = mToxForm.GetPattern(nLvl);
@@ -228,9 +227,9 @@ ToxTextGenerator::GenerateText(SwDoc* pDoc,
 
             case TOKEN_TAB_STOP: {
                 ToxTabStopTokenHandler::HandledTabStopToken htst =
-                        mTabStopTokenHandler->HandleTabStopToken(aToken, 
*pTOXNd, pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+                    mTabStopTokenHandler->HandleTabStopToken(aToken, *pTOXNd);
                 rText += htst.text;
-                aTStops.Insert(htst.tabStop);
+                oRet->second.Insert(htst.tabStop);
                 break;
             }
 
@@ -304,10 +303,9 @@ ToxTextGenerator::GenerateText(SwDoc* pDoc,
                 }
             }
         }
-
-        pTOXNd->SetAttr( aTStops );
     }
     mLinkProcessor->InsertLinkAttributes(*pTOXNd);
+    return oRet;
 }
 
 /*static*/ std::shared_ptr<SfxItemSet>

Reply via email to