[Libreoffice-commits] core.git: Branch 'distro/nisz/libreoffice-7-1' - svx/source sw/inc sw/qa sw/source

Attila Bakos (NISZ) (via logerrit) logerrit at kemper.freedesktop.org
Thu Aug 19 13:54:57 UTC 2021


 svx/source/svdraw/svdpage.cxx                        |    5 -
 sw/inc/textboxhelper.hxx                             |    4 
 sw/qa/extras/ooxmlexport/data/testTextBoxZOrder.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport16.cxx           |   31 +++++++
 sw/source/core/doc/textboxhelper.cxx                 |   70 ++++++++++++++--
 sw/source/core/frmedt/feshview.cxx                   |   82 +++++++++++++++++++
 6 files changed, 185 insertions(+), 7 deletions(-)

New commits:
commit 09aba686b452e3fa2d7ad3e06ebebba83aa0dfe7
Author:     Attila Bakos (NISZ) <bakos.attilakaroly at nisz.hu>
AuthorDate: Fri May 7 10:18:01 2021 +0200
Commit:     Gabor Kelemen <kelemen.gabor2 at nisz.hu>
CommitDate: Thu Aug 19 15:54:19 2021 +0200

    tdf#138141 sw: fix textbox z-order
    
    Textboxes are implemented as loosely connected
    shape-text frame pairs. Missing synchronization
    of their z-orders resulted e.g invisible or
    only partially visible textbox content using
    Arrange options with textboxes (see in local menu
    or on Drawing Object Properties toolbar).
    
    Note: because it's not possible to send frames
    to the background, Arrange->To Background hasn't
    supported, so likely it's worth to remove that
    option later from local menu of textboxes.
    
    Change-Id: I1aa50903ba55dd5b9e72ef203c4e30218bee68fb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115227
    Tested-by: László Németh <nemeth at numbertext.org>
    Reviewed-by: László Németh <nemeth at numbertext.org>
    (cherry picked from commit 0e6d963fbca16f98a3dbb6ef2fee3736a89d055b)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120713
    Tested-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>
    Reviewed-by: Gabor Kelemen <kelemen.gabor2 at nisz.hu>

diff --git a/svx/source/svdraw/svdpage.cxx b/svx/source/svdraw/svdpage.cxx
index c1f8980f4236..be6907184c08 100644
--- a/svx/source/svdraw/svdpage.cxx
+++ b/svx/source/svdraw/svdpage.cxx
@@ -757,7 +757,10 @@ size_t SdrObjList::GetObjCount() const
 
 SdrObject* SdrObjList::GetObj(size_t nNum) const
 {
-    return maList[nNum];
+    if (nNum < maList.size())
+        return maList[nNum];
+
+    return nullptr;
 }
 
 
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index 90e173046133..6bfd8ddfad00 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -123,6 +123,10 @@ public:
     /// Returns true if the given shape has a valid textframe.
     static bool isTextBoxShapeHasValidTextFrame(css::uno::Reference<css::drawing::XShape> xShape);
 
+    // Returns true on success. Synchronize z-order of the text frame of the given textbox
+    // by setting it one level higher than the z-order of the shape of the textbox.
+    static bool DoTextBoxZOrderCorrection(SwFrameFormat* pShape);
+
     /**
      * If we have an associated TextFrame, then return that.
      *
diff --git a/sw/qa/extras/ooxmlexport/data/testTextBoxZOrder.docx b/sw/qa/extras/ooxmlexport/data/testTextBoxZOrder.docx
new file mode 100644
index 000000000000..c6dd0b8f30c8
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/testTextBoxZOrder.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index f0664b1f002d..7e890145210d 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -18,6 +18,7 @@
 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
 #include <com/sun/star/text/XTextTable.hpp>
 #include <com/sun/star/text/XTextTablesSupplier.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
 #include <comphelper/configuration.hxx>
 #include <editeng/escapementitem.hxx>
@@ -160,6 +161,36 @@ DECLARE_OOXMLEXPORT_TEST(testTdf133473_shadowSize, "tdf133473.docx")
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(200000), nSize1);
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTextBoxZOrder, "testTextBoxZOrder.docx")
+{
+    // Is load successful?
+    CPPUNIT_ASSERT(mxComponent);
+    // Collect the z-order values of the textboxes
+    std::vector<sal_uInt64> ShapeZorders;
+    std::vector<sal_uInt64> FrameZorders;
+    for (int i = 1; i < 4; i++)
+    {
+        uno::Reference<drawing::XShape> xShape(getShape(i));
+        CPPUNIT_ASSERT(xShape);
+        uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
+        CPPUNIT_ASSERT(xShapeProperties);
+        uno::Reference<text::XTextFrame> xFrame = SwTextBoxHelper::getUnoTextFrame(xShape);
+        CPPUNIT_ASSERT(xFrame.is());
+        uno::Reference<beans::XPropertySet> const xFrameProperties(xFrame, uno::UNO_QUERY);
+        CPPUNIT_ASSERT(xFrameProperties);
+        ShapeZorders.push_back(xShapeProperties->getPropertyValue("ZOrder").get<sal_uInt64>());
+        FrameZorders.push_back(xFrameProperties->getPropertyValue("ZOrder").get<sal_uInt64>());
+    }
+    // Check the z-order values.
+    for (int i = 1; i < 3; i++)
+    {
+        CPPUNIT_ASSERT_GREATER(ShapeZorders[i - 1], ShapeZorders[i]);
+        CPPUNIT_ASSERT_GREATER(FrameZorders[i - 1], FrameZorders[i]);
+        CPPUNIT_ASSERT_GREATER(ShapeZorders[i - 1], FrameZorders[i - 1]);
+    }
+    // Without the fix it failed, because the z-order was wrong.
+}
+
 DECLARE_OOXMLEXPORT_TEST(testTdf141550, "tdf141550.docx")
 {
     uno::Reference<drawing::XShape> xShape(getShape(1));
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 88138f6aee10..e5ade0b38125 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -39,6 +39,9 @@
 #include <sal/log.hxx>
 #include <tools/UnitConversion.hxx>
 #include <svx/swframetypes.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentDrawModelManager.hxx>
 
 #include <com/sun/star/document/XActionLockable.hpp>
 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
@@ -136,6 +139,7 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, bool bCopyText)
         pShape->SetFormatAttr(aSet);
     }
 
+    DoTextBoxZOrderCorrection(pShape);
     // Also initialize the properties, which are not constant, but inherited from the shape's ones.
     uno::Reference<drawing::XShape> xShape(pShape->FindRealSdrObject()->getUnoShape(),
                                            uno::UNO_QUERY);
@@ -960,6 +964,8 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&
 
     if (aTextBoxSet.Count())
         pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet);
+
+    DoTextBoxZOrderCorrection(&rShape);
 }
 
 SwFrameFormat* SwTextBoxHelper::getShapeFormat(uno::Reference<drawing::XShape> xShape)
@@ -1019,6 +1025,7 @@ bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape)
     {
         if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
         {
+            ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
             if (auto xFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat))
                 try
                 {
@@ -1070,6 +1077,7 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape)
             {
                 try
                 {
+                    ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
                     uno::Reference<beans::XPropertySet> const xPropertySet(
                         SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat),
                         uno::UNO_QUERY);
@@ -1136,7 +1144,7 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape)
                 }
             }
 
-            return doTextBoxPositioning(pShape);
+            return doTextBoxPositioning(pShape) && DoTextBoxZOrderCorrection(pShape);
         }
     }
     return false;
@@ -1155,6 +1163,7 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape)
     {
         if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
         {
+            ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
             if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
             {
                 tools::Rectangle aRect(getTextRectangle(pShape, false));
@@ -1224,19 +1233,68 @@ std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(SwFrameFormat* pShape
 
 bool SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(SwFrameFormat* pShape)
 {
-    OUString sErrMsg;
     if (pShape && pShape->Which() == RES_DRAWFRMFMT)
         if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT))
             if (pFormat && pFormat->Which() == RES_FLYFRMFMT)
                 return true;
             else
-                sErrMsg = "Shape do not have valid textframe!";
+                SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: "
+                                    "Shape does not have valid textframe!");
         else
-            sErrMsg = "Shape do not have associated frame!";
+            SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: "
+                                "Shape does not have associated frame!");
     else
-        sErrMsg = "Not valid shape!";
+        SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Not valid shape!");
+    return false;
+}
+
+bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape)
+{
+    if (isTextBoxShapeHasValidTextFrame(pShape))
+    {
+        if (SdrObject* pShpObj = pShape->FindRealSdrObject())
+        {
+            if (SdrObject* pFrmObj
+                = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)->FindRealSdrObject())
+            {
+                // Get the draw model from the doc
+                SwDrawModel* pDrawModel
+                    = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
+                if (pDrawModel)
+                {
+                    // Not really sure this will work all page, but it seems it will.
+                    auto pPage = pDrawModel->GetPage(0);
+                    // Recalc all Zorders
+                    pPage->RecalcObjOrdNums();
+                    // If the shape is behind the frame, is good, but if there are some objects
+                    // between of them that is wrong so put the frame exactly one level higher
+                    // than the shape.
+                    if (pFrmObj->GetOrdNum() > pShpObj->GetOrdNum())
+                        pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pShpObj->GetOrdNum() + 1);
+                    else
+                        // Else, if the frame is behind the shape, bring to the front of it.
+                        while (pFrmObj->GetOrdNum() <= pShpObj->GetOrdNum())
+                        {
+                            pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pFrmObj->GetOrdNum() + 1);
+                            // If there is any problem with the indexes, do not run over the infinity
+                            if (pPage->GetObjCount() == pFrmObj->GetOrdNum())
+                                break;
+                        }
+                    pPage->RecalcObjOrdNums();
+                    return true; // Success
+                }
+                SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+                                    "No Valid Draw model for SdrObject for the shape!");
+            }
+            SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+                                "No Valid SdrObject for the frame!");
+        }
+        SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+                            "No Valid SdrObject for the shape!");
+    }
+    SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+                        "No Valid TextFrame!");
 
-    SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: " << sErrMsg);
     return false;
 }
 
diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx
index 439d99e9e2d7..9d907ea24fc7 100644
--- a/sw/source/core/frmedt/feshview.cxx
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -1055,6 +1055,55 @@ void SwFEShell::SelectionToTop( bool bTop )
     else
         Imp()->GetDrawView()->MovMarkedToTop();
     ::lcl_NotifyNeighbours( &rMrkList );
+
+    // Does the selection contain a textbox?
+    for (size_t i = 0; i < rMrkList.GetMarkCount(); i++)
+        if (auto pObj = rMrkList.GetMark(i)->GetMarkedSdrObj())
+            // Get the textbox-shape
+            if (auto pFormat = FindFrameFormat(pObj))
+            {
+                // If it has not textframe skip...
+                if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat))
+                    continue;
+                // If it has a textframe so it is a textbox, get its page
+                if (auto pDrwModel
+                    = pFormat->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel())
+                    // Not really understood why everything is on page 0...
+                    // but it is easier to handle sdrobjects, thats true
+                    if (auto pPage = pDrwModel->GetPage(0))
+                    {
+                        // nShift: it means how many layers the pObj have to be shifted up,
+                        // in order not to interfere with other shapes and textboxes.
+                        // Situations:
+                        // - The next shape has textframe: This shape have to shifted with
+                        //   two layers.
+                        // - The next shape has not got textframe: This shape have to be
+                        //   shifted only one layer up.
+                        // - The next shape is null:
+                        //      - This shape is already at heaven: Only the textframe have
+                        //        to be adjusted.
+                        sal_uInt32 nShift = 0;
+                        // Get the one level higher object (note: can be nullptr!)
+                        const auto pNextObj = pPage->SetObjectOrdNum(pObj->GetOrdNum() + 1, pObj->GetOrdNum() + 1);
+                        // If there is a higher object (not null)...
+                        if (pNextObj)
+                        {
+                            // One level shift is neccessary
+                            nShift++;
+                            // If this object is a textbox, two level increasing needed
+                            // (one for the shape and one for the frame)
+                            if (auto pNextFormat = FindFrameFormat(pNextObj))
+                                if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT)
+                                    || SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT))
+                                    nShift++;
+                        }
+                        // Set the new z-order.
+                        pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() + nShift);
+                    }
+                // The shape is on the right level, correct the layer of the frame
+                SwTextBoxHelper::DoTextBoxZOrderCorrection(pFormat);
+            }
+
     GetDoc()->getIDocumentState().SetModified();
     EndAllAction();
 }
@@ -1075,6 +1124,36 @@ void SwFEShell::SelectionToBottom( bool bBottom )
     else
         Imp()->GetDrawView()->MovMarkedToBtm();
     ::lcl_NotifyNeighbours( &rMrkList );
+
+    // If the selection has textbox
+    for(size_t i = 0; i < rMrkList.GetMarkCount(); i++)
+        if (auto pObj = rMrkList.GetMark(i)->GetMarkedSdrObj())
+            // Get the shape of the textbox
+            if (auto pFormat = FindFrameFormat(pObj))
+            {
+                // If the shape has not textframes skip.
+                if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat))
+                    continue;
+                // If has, move the shape to correct level with...
+                if (auto pDrwModel
+                    = pFormat->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel())
+                    if (auto pPage = pDrwModel->GetPage(0))
+                    {
+                        const auto pNextObj = pPage->SetObjectOrdNum(pObj->GetOrdNum() - 1, pObj->GetOrdNum() - 1);
+                        // If there is a lower object (not null)...
+                        if (pNextObj)
+                        {
+                            // If the lower has no textframe, just do nothing, else move by one lower
+                            if (auto pNextFormat = FindFrameFormat(pNextObj))
+                                if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT)
+                                    || SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT))
+                                    pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() - 1);
+                        }
+                    }
+                // And set correct layer for the selected textbox.
+                SwTextBoxHelper::DoTextBoxZOrderCorrection(pFormat);
+            }
+
     GetDoc()->getIDocumentState().SetModified();
     EndAllAction();
 }
@@ -1138,6 +1217,9 @@ void SwFEShell::ChangeOpaque( SdrLayerID nLayerId )
                 SvxOpaqueItem aOpa( pFormat->GetOpaque() );
                 aOpa.SetValue(  nLayerId == rIDDMA.GetHellId() );
                 pFormat->SetFormatAttr( aOpa );
+                // If pObj has textframe, put its textframe to the right level
+                if (auto pTextBx = FindFrameFormat(pObj))
+                    SwTextBoxHelper::DoTextBoxZOrderCorrection(pTextBx);
             }
         }
     }


More information about the Libreoffice-commits mailing list