summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorAttila Bakos (NISZ) <bakos.attilakaroly@nisz.hu>2021-05-07 10:18:01 +0200
committerLászló Németh <nemeth@numbertext.org>2021-05-25 13:06:22 +0200
commit0e6d963fbca16f98a3dbb6ef2fee3736a89d055b (patch)
treee1a413ce8f1307cbbcd8cf96af1d130dcce3a469 /sw
parentb2f7187e04a6b6c6183b42230a0119569f069dcf (diff)
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@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org>
Diffstat (limited to 'sw')
-rw-r--r--sw/inc/textboxhelper.hxx4
-rw-r--r--sw/qa/extras/ooxmlexport/data/testTextBoxZOrder.docxbin0 -> 17635 bytes
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport16.cxx31
-rw-r--r--sw/source/core/doc/textboxhelper.cxx70
-rw-r--r--sw/source/core/frmedt/feshview.cxx82
5 files changed, 181 insertions, 6 deletions
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index 0db804424332..1c9fdc66e703 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -107,6 +107,10 @@ public:
/// Returns true if the given shape has a valid textframe.
static bool isTextBoxShapeHasValidTextFrame(SwFrameFormat* pShape);
+ // 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
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/testTextBoxZOrder.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 16cd111e9e54..165f104c2f57 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -26,6 +26,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 <comphelper/propertysequence.hxx>
@@ -338,6 +339,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 cba67e1bf74d..f901beab461c 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -38,6 +38,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>
@@ -135,6 +138,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);
@@ -1016,6 +1020,8 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&
if (aTextBoxSet.Count())
pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet);
+
+ DoTextBoxZOrderCorrection(&rShape);
}
void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
@@ -1065,6 +1071,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
{
@@ -1109,6 +1116,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);
@@ -1175,7 +1183,7 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape)
}
}
- return doTextBoxPositioning(pShape);
+ return doTextBoxPositioning(pShape) && DoTextBoxZOrderCorrection(pShape);
}
}
return false;
@@ -1187,6 +1195,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));
@@ -1249,19 +1258,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 31c30e3c31ec..6f4dbf238612 100644
--- a/sw/source/core/frmedt/feshview.cxx
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -1056,6 +1056,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();
}
@@ -1076,6 +1125,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();
}
@@ -1139,6 +1218,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);
}
}
}