sw/inc/textboxhelper.hxx | 74 +-- sw/inc/unomid.h | 4 sw/inc/unoprnms.hxx | 1 sw/qa/uitest/data/ComplexGroupShapeTest.odt |binary sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py | 124 +++++ sw/source/core/doc/DocumentLayoutManager.cxx | 45 + sw/source/core/doc/docdraw.cxx | 55 +- sw/source/core/doc/docfly.cxx | 11 sw/source/core/doc/textboxhelper.cxx | 492 +++++++++++++++----- sw/source/core/draw/dcontact.cxx | 11 sw/source/core/draw/dview.cxx | 8 sw/source/core/frmedt/feshview.cxx | 4 sw/source/core/layout/atrfrm.cxx | 5 sw/source/core/unocore/unodraw.cxx | 66 +- sw/source/core/unocore/unomap.cxx | 3 sw/source/core/unocore/unomap1.cxx | 1 16 files changed, 702 insertions(+), 202 deletions(-)
New commits: commit eabcfb3f18a6944d9ad89cecd3eb3ca7a2259cf3 Author: Attila Bakos (NISZ) <bakos.attilakar...@nisz.hu> AuthorDate: Wed Nov 3 15:39:32 2021 +0100 Commit: László Németh <nem...@numbertext.org> CommitDate: Wed Nov 24 11:51:07 2021 +0100 tdf#129183 sw: textboxes in group shapes - part 3 Grouping/ungrouping nested groups works now. Manual test: 1. Insert Shape. 2. Right-click on selected shape, Add Text Box (and some text). 3. Insert a new shape. 4. Select and group the two shapes. 3. Insert a third shape. 4. Select and group the shape and the previously grouped shapes. The text box remains in the nested shape group. Details: 1) tdf#144271 memory leak of SwTextBoxHelper, by replacing the textbox structure vector with std::unordered map, and rethinking of the ownership of the objects. If a SwFrameFormat dies, and that is a FLYFRMFMT, it will be deleted from the textbox node and the FrameFormat table in the doc too, and the drawing will be stay as it was before. If the dying format is a drawing, all the textboxes, and the node will be deleted. 2) Introducing the new UNO property TextBoxContent, which is needed for writerfilter/xmloff later to set a new textbox for the shape via UNO. 3) Missing parameters are present now for syncing the textbox parameters. 4) Introducing a new function namely the handleGroupTextBox() to do the tasks simply with all textboxes in a group shape. This can handle nested groups as well (group in a group). Known issues: now copy of nested group objects is implemented but not enabled, because it causes an assert. Change-Id: I931886eda01c7a3db93098de10f5e5f48f2f217b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124657 Tested-by: László Németh <nem...@numbertext.org> Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx index 2e5b27cfccb0..10841ed8626c 100644 --- a/sw/inc/textboxhelper.hxx +++ b/sw/inc/textboxhelper.hxx @@ -11,6 +11,7 @@ #define INCLUDED_SW_INC_TEXTBOXHELPER_HXX #include <map> +#include <unordered_map> #include <optional> #include <set> #include <vector> @@ -60,12 +61,16 @@ public: /// the original text in the shape will be copied to the frame /// The textbox is created for the shape given by the pObject parameter. static void create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText = false); + /// Sets the given textframe as textbox for the given (group member) shape. + static void set(SwFrameFormat* pShape, SdrObject* pObject, + css::uno::Reference<css::text::XTextFrame> xNew); /// Destroy a TextBox for a shape. If the format has more textboxes /// like group shapes, it will destroy only that textbox what belongs /// to the given pObject shape. static void destroy(const SwFrameFormat* pShape, const SdrObject* pObject); /// Get interface of a shape's TextBox, if there is any. - static css::uno::Any queryInterface(const SwFrameFormat* pShape, const css::uno::Type& rType); + static css::uno::Any queryInterface(const SwFrameFormat* pShape, const css::uno::Type& rType, + SdrObject* pObj = nullptr); /// Sync property of TextBox with the one of the shape. static void syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID, @@ -90,7 +95,7 @@ public: /// Sets the surround to through for the textframe of the given shape, /// not to interfere with the layout. Returns true on success. - static bool setWrapThrough(SwFrameFormat* pShape); + static bool setWrapThrough(SwFrameFormat* pShape, SdrObject* pObj = nullptr); /// Sets the anchor of the associated textframe of the given shape, and /// returns true on success. @@ -100,15 +105,20 @@ public: /// returns true on success. static bool doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj); + /// Sets the correct size of textframe depending on the given SdrObject. + static bool setTextBoxSize(const SwFrameFormat* pShape, SdrObject* pObj); + /// Returns true if the anchor different for the given shape, and the /// associated textframe of the given shape. /// Note: In case of AS_CHAR anchor the anchor type must be different, /// because if not, layout breaks, but this situation also handled by /// this function, and returns true in that case too. - static std::optional<bool> isAnchorTypeDifferent(const SwFrameFormat* pShape); + static std::optional<bool> isAnchorTypeDifferent(const SwFrameFormat* pShape, + SdrObject* pObj = nullptr); /// Returns true if the given shape has a valid textframe. - static bool isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape); + static bool isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape, + SdrObject* pObj = nullptr); // 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. @@ -177,6 +187,26 @@ public: /// Undo the effect of saveLinks() + individual resetLink() calls. static void restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew, SavedLink& rSavedLinks); + + /// The following actions are implemented for groupshapes with textboxes too. + /// The selected action will be done for all of the group member textboxes. + enum GroupTextBoxActionType + { + POSITION_SIZE_AND_ANCHOR_CHANGE, + DELETE, + Z_ORDER_CHANGE + }; + + /// Does the selected action with ALL textboxes in the group. + /// Parameters: + /// - pGroupShapeFormat: The frame format of the group shape where the textboxes belongs to. + /// - pGroupObject: The drawing object for the group. + /// - eActionType: The action what the function is supposed to do. + /// WARN: This function will run recursive! ALL textboxes of the group will be handled by + /// the desired action! + static void handleTextBoxGroup(SwFrameFormat* pGroupShapeFormat, + GroupTextBoxActionType eActionType, + SdrObject* pGroupObject = nullptr); }; /// Textboxes are basically textframe + shape pairs. This means one shape has one frame. @@ -185,27 +215,15 @@ public: /// it can have multiple textboxes. class SwTextBoxNode { - // One TextBox-entry - struct SwTextBoxElement - { - // The textframe format - SwFrameFormat* m_pTextBoxFormat; - // The Draw object where the textbox belongs to - SdrObject* m_pDrawObject; - // This is for indicating if the textbox is in special case: for example during undo. - bool m_bIsActive; - }; - - // This vector stores the textboxes what belongs to this node - std::vector<SwTextBoxElement> m_pTextBoxes; + // This map stores the textboxes what belongs to this node + std::unordered_map<const SdrObject*, SwFrameFormat*> m_pTextBoxTable; // This is the pointer to the shape format, which has this node // (and the textboxes) SwFrameFormat* m_pOwnerShapeFormat; - -public: // Not needed. SwTextBoxNode() = delete; +public: // ctor SwTextBoxNode(SwFrameFormat* pOwnerShapeFormat); // dtor @@ -224,24 +242,24 @@ public: // Parameters: // pDrawObject: The shape which have the textbox to be deleted. void DelTextBox(const SdrObject* pDrawObject); + void DelTextBox(SwFrameFormat* pTextBox); // This will return with the frame format of the textbox what belongs // to the given shape (pDrawObject) SwFrameFormat* GetTextBox(const SdrObject* pDrawObject) const; - // Is this textbox has special state, undo for example? - bool IsTextBoxActive(const SdrObject* pDrawObject) const; - - // Setters for the state flag. - void SetTextBoxInactive(const SdrObject* pDrawObject); - void SetTextBoxActive(const SdrObject* pDrawObject); - // If this is a group shape, that returns true. - bool IsGroupTextBox() const; + bool IsGroupTextBoxShape() const; // This returns with the shape what this class belongs to. SwFrameFormat* GetOwnerShape() { return m_pOwnerShapeFormat; }; // This will give the current number of textboxes. - size_t GetTextBoxCount() const { return m_pTextBoxes.size(); }; + size_t GetTextBoxCount() const { return m_pTextBoxTable.size(); }; + + // Gives a const reference to the text box table, useful for undo and grouping. + const std::unordered_map<const SdrObject*, SwFrameFormat*>& GetTextBoxTable() const + { + return m_pTextBoxTable; + }; }; #endif // INCLUDED_SW_INC_TEXTBOXHELPER_HXX diff --git a/sw/inc/unomid.h b/sw/inc/unomid.h index d249b32fc25a..5b6e8a0cb6cd 100644 --- a/sw/inc/unomid.h +++ b/sw/inc/unomid.h @@ -151,6 +151,10 @@ // SwFormatFollowTextFlow #define MID_FOLLOW_TEXT_FLOW 0 +// TextBox +#define MID_TEXTBOX 0 +#define MID_TEXTBOX_CONTENT 1 + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 7ef5d0a58cd7..1e16bc413d01 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -74,6 +74,7 @@ #define UNO_NAME_FOOTER_RIGHT_MARGIN "FooterRightMargin" #define UNO_NAME_TEXT_RANGE "TextRange" #define UNO_NAME_TEXT_BOX "TextBox" +#define UNO_NAME_TEXT_BOX_CONTENT "TextBoxContent" #define UNO_NAME_NAME "Name" #define UNO_NAME_CHAR_STYLE_NAME "CharStyleName" #define UNO_NAME_ANCHOR_CHAR_STYLE_NAME "AnchorCharStyleName" diff --git a/sw/qa/uitest/data/ComplexGroupShapeTest.odt b/sw/qa/uitest/data/ComplexGroupShapeTest.odt new file mode 100644 index 000000000000..8fe093203690 Binary files /dev/null and b/sw/qa/uitest/data/ComplexGroupShapeTest.odt differ diff --git a/sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py b/sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py new file mode 100644 index 000000000000..cdac088a32d7 --- /dev/null +++ b/sw/qa/uitest/writer_tests4/ComplexGroupShapeTest.py @@ -0,0 +1,124 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +from uitest.framework import UITestCase +from uitest.uihelper.common import get_state_as_dict +from uitest.uihelper.common import select_pos +from uitest.uihelper.common import get_url_for_data_file +from libreoffice.uno.propertyvalue import mkPropertyValues +import time + +class ComplexGroupShapeTest(UITestCase): + def test_ComplexGroupShape(self): + with self.ui_test.load_file(get_url_for_data_file("ComplexGroupShapeTest.odt")): + xWriterDoc = self.xUITest.getTopFocusWindow() + xWriterEdit = xWriterDoc.getChild("writer_edit") + document = self.ui_test.get_component() + + # check the shape type + self.assertEqual("com.sun.star.drawing.GroupShape", document.DrawPage.getByIndex(1).ShapeType) + + # select the shape + self.xUITest.executeCommand(".uno:JumpToNextFrame") + self.ui_test.wait_until_child_is_available('metricfield') + + # go inside the group + self.xUITest.executeCommand(".uno:EnterGroup") + + # select a shape in the group + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + + # add a textbox to this subshape + self.xUITest.executeCommand(".uno:AddTextBox") + + # select the next shape in the group + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + + # add a textbox to this subshape + self.xUITest.executeCommand(".uno:AddTextBox") + + # leave the groupshape + self.xUITest.executeCommand(".uno:LeaveGroup") + + # select the other shape + self.xUITest.executeCommand(".uno:JumpToNextFrame") + self.ui_test.wait_until_child_is_available('metricfield') + + # get the current selection + ShapeCollection = document.getCurrentSelection() + + # extend the selection with the grouped shape + ShapeCollection.add(document.DrawPage.getByIndex(0)) + ShapeCollection.add(document.DrawPage.getByIndex(1)) + + # select these shapes + document.getCurrentController().select(ShapeCollection) + + # do ungroup + self.xUITest.executeCommand(".uno:FormatGroup") + + # deselect + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"ESC"})) + time.sleep(0.1) + + # select the group + self.xUITest.executeCommand(".uno:JumpToNextFrame") + self.ui_test.wait_until_child_is_available('metricfield') + + # move it down + for i in range(1, 30): + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"DOWN"})) + time.sleep(0.1) + + # select again + self.xUITest.executeCommand(".uno:JumpToNextFrame") + self.ui_test.wait_until_child_is_available('metricfield') + + # do ungroup + self.xUITest.executeCommand(".uno:FormatUngroup") + + # deselect everything + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"ESC"})) + time.sleep(0.1) + + # select the first ex-group member shape + self.xUITest.executeCommand(".uno:JumpToNextFrame") + self.ui_test.wait_until_child_is_available('metricfield') + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + + # check if it is a textbox + self.assertEqual(True,document.getCurrentSelection().getByIndex(0).TextBox) + + # go to the other one + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + + # this is still a group, so it cannot be a textbox + self.assertEqual(False,document.getCurrentSelection().getByIndex(0).TextBox) + + # do ungroup + self.xUITest.executeCommand(".uno:FormatUngroup") + + # deselect + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE":"ESC"})) + time.sleep(0.1) + + # select one shape of the last group + self.xUITest.executeCommand(".uno:JumpToNextFrame") + self.ui_test.wait_until_child_is_available('metricfield') + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + xWriterEdit.executeAction("TYPE", mkPropertyValues({"KEYCODE": "TAB"})) + + # check if it is a textbox + self.assertEqual(True,document.getCurrentSelection().getByIndex(0).TextBox) + + # Without the fix in place, the following problems occurred during this test: + # - After the grouping old textbox frames detached from their shape before + # - Moving caused messed layout + # - After ungroup, the shapes in the embed group lost their textbox + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx b/sw/source/core/doc/DocumentLayoutManager.cxx index c67e9e05e9a6..ae49a77183c0 100644 --- a/sw/source/core/doc/DocumentLayoutManager.cxx +++ b/sw/source/core/doc/DocumentLayoutManager.cxx @@ -312,7 +312,39 @@ void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat *pFormat ) } m_rDoc.getIDocumentState().SetModified(); } - +#if 0 +// TODO: Replace the textbox part of frameformat copying method to this. +// Now disabled because cause asserts in sw (Node index is still registered in dtor). +// Without this nested group textboxes are not handled correctly. +static void lcl_CopyTextBoxes(SwDoc* pDoc, SwTextBoxNode* pSrcTextBoxes, const SdrObject* pSrcObj, + SwTextBoxNode* pDestTextBoxes, SdrObject* pDestObj, + bool bSetTextFlyAtt, bool bMakeFrames) +{ + auto pSrcChildren = pSrcObj->getChildrenOfSdrObject(); + auto pDestChildren = pDestObj->getChildrenOfSdrObject(); + if (pSrcChildren && pDestChildren) + { + if (pSrcChildren->GetObjCount() == pDestChildren->GetObjCount()) + for (size_t i = 0; i < pSrcChildren->GetObjCount(); ++i) + { + lcl_CopyTextBoxes(pDoc, pSrcTextBoxes, pSrcChildren->GetObj(i), pDestTextBoxes, + pDestChildren->GetObj(i), bSetTextFlyAtt, bMakeFrames); + } + } + else + { + if (auto pSourceTextBoxFormat = pSrcTextBoxes->GetTextBox(pSrcObj)) + { + assert(pSourceTextBoxFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR); + auto pNewTextBoxFormat = pDoc->GetDocumentLayoutManager().CopyLayoutFormat( + *pSourceTextBoxFormat, pSourceTextBoxFormat->GetAnchor(), bSetTextFlyAtt, + bMakeFrames); + pDestTextBoxes->AddTextBox(pDestObj, pNewTextBoxFormat); + pNewTextBoxFormat->SetOtherTextBoxFormat(pDestTextBoxes); + } + } +} +#endif /** Copies the stated format (pSrc) to pDest and returns pDest. If there's no pDest, it is created. @@ -469,7 +501,18 @@ SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat( auto pObj = rSource.FindRealSdrObject(); auto pTextBoxNd = new SwTextBoxNode(pDest); pDest->SetOtherTextBoxFormat(pTextBoxNd); +#if 0 + // TODO: Change the copy algorithm to this. See comment in lcl_CopyTextBoxes for details. + if (!bMakeFrames && rNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR) + { + // If the draw format is as-char, then it will be copied with bMakeFrames=false, but + // doing the same for the fly format would result in not making fly frames at all. + bMakeFrames = true; + } + lcl_CopyTextBoxes(&m_rDoc, rSource.GetOtherTextBoxFormat(), rSource.FindRealSdrObject(), + pTextBoxNd, pDest->FindRealSdrObject(), bSetTextFlyAtt, bMakeFrames); +#endif if (pObj) { const bool bIsGroupObj = pObj->getChildrenOfSdrObject(); diff --git a/sw/source/core/doc/docdraw.cxx b/sw/source/core/doc/docdraw.cxx index 0aff4b8993ff..39adffb8b5a0 100644 --- a/sw/source/core/doc/docdraw.cxx +++ b/sw/source/core/doc/docdraw.cxx @@ -209,7 +209,7 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView ) bGroupMembersNotPositioned = pAnchoredDrawObj->NotYetPositioned(); } - std::vector<std::pair<SwFrameFormat*, SdrObject*>> vSavedTextBoxes; + std::unordered_map<const SdrObject*, SwFrameFormat*> vSavedTextBoxes; // Destroy ContactObjects and formats. for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) { @@ -224,8 +224,13 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView ) "<SwDoc::GroupSelection(..)> - group members have different positioning status!" ); #endif // Before the format will be killed, save its textbox for later use. - if (auto pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pContact->GetFormat(), RES_DRAWFRMFMT, pObj)) - vSavedTextBoxes.push_back(std::pair<SwFrameFormat*, SdrObject*>(pTextBox, pObj)); + if (auto pTxBxNd = pContact->GetFormat()->GetOtherTextBoxFormat()) + { + for (auto& rElem : pTxBxNd->GetTextBoxTable()) + { + vSavedTextBoxes.emplace(rElem); + } + } pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat()); // Deletes itself! @@ -256,8 +261,8 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView ) auto pTextBoxNode = new SwTextBoxNode(pFormat); for (auto& pTextBoxEntry : vSavedTextBoxes) { - pTextBoxNode->AddTextBox(pTextBoxEntry.second, pTextBoxEntry.first); - pTextBoxEntry.first->SetOtherTextBoxFormat(pTextBoxNode); + pTextBoxNode->AddTextBox(const_cast<SdrObject*>(pTextBoxEntry.first), pTextBoxEntry.second); + pTextBoxEntry.second->SetOtherTextBoxFormat(pTextBoxNode); } pFormat->SetOtherTextBoxFormat(pTextBoxNode); vSavedTextBoxes.clear(); @@ -299,6 +304,27 @@ SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView ) return pNewContact; } +static void lcl_CollectTextBoxesForSubGroupObj(SwFrameFormat* pTargetFormat, SwTextBoxNode* pTextBoxNode, + SdrObject* pSourceObjs) +{ + if (auto pChildrenObjs = pSourceObjs->getChildrenOfSdrObject()) + for (size_t i = 0; i < pChildrenObjs->GetObjCount(); ++i) + lcl_CollectTextBoxesForSubGroupObj(pTargetFormat, pTextBoxNode, pChildrenObjs->GetObj(i)); + else + { + if (auto pTextBox = pTextBoxNode->GetTextBox(pSourceObjs)) + { + if (!pTargetFormat->GetOtherTextBoxFormat()) + { + pTargetFormat->SetOtherTextBoxFormat(new SwTextBoxNode(pTargetFormat)); + } + + pTargetFormat->GetOtherTextBoxFormat()->AddTextBox(pSourceObjs, pTextBox); + pTextBox->SetOtherTextBoxFormat(pTargetFormat->GetOtherTextBoxFormat()); + } + } +} + void SwDoc::UnGroupSelection( SdrView& rDrawView ) { bool const bUndo = GetIDocumentUndoRedo().DoesUndo(); @@ -349,13 +375,22 @@ void SwDoc::UnGroupSelection( SdrView& rDrawView ) pFormat->SetFormatAttr( aAnch ); if (pTextBoxNode) - if (auto pTextBoxFormat = pTextBoxNode->GetTextBox(pSubObj)) + { + if (!pSubObj->getChildrenOfSdrObject()) + { + if (auto pTextBoxFormat = pTextBoxNode->GetTextBox(pSubObj)) + { + auto pNewTextBoxNode = new SwTextBoxNode(pFormat); + pNewTextBoxNode->AddTextBox(pSubObj, pTextBoxFormat); + pFormat->SetOtherTextBoxFormat(pNewTextBoxNode); + pTextBoxFormat->SetOtherTextBoxFormat(pNewTextBoxNode); + }; + } + else { - auto pNewTextBoxNode = new SwTextBoxNode(pFormat); - pNewTextBoxNode->AddTextBox(pSubObj, pTextBoxFormat); - pFormat->SetOtherTextBoxFormat(pNewTextBoxNode); - pTextBoxFormat->SetOtherTextBoxFormat(pNewTextBoxNode); + lcl_CollectTextBoxesForSubGroupObj(pFormat, pTextBoxNode, pSubObj); } + } // #i36010# - set layout direction of the position pFormat->SetPositionLayoutDir( diff --git a/sw/source/core/doc/docfly.cxx b/sw/source/core/doc/docfly.cxx index 6b73f1f756b5..16365ebbbfec 100644 --- a/sw/source/core/doc/docfly.cxx +++ b/sw/source/core/doc/docfly.cxx @@ -577,8 +577,6 @@ bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet ) getIDocumentState().SetModified(); - //SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet); - return bRet; } @@ -920,12 +918,9 @@ bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList, pNd->InsertItem( aFormat, aPos.nContent.GetIndex(), 0 ); // Has a textbox attached to the format? Sync it as well! - if (SwTextBoxHelper::getOtherTextBoxFormat(pContact->GetFormat(), - RES_DRAWFRMFMT)) - { - SwTextBoxHelper::syncFlyFrameAttr(*pContact->GetFormat(), - pContact->GetFormat()->GetAttrSet(), pObj); - } + SwTextBoxHelper::syncFlyFrameAttr(*pContact->GetFormat(), + pContact->GetFormat()->GetAttrSet(), pObj); + } break; default: diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx index 418c69927228..8bd107623b8a 100644 --- a/sw/source/core/doc/textboxhelper.cxx +++ b/sw/source/core/doc/textboxhelper.cxx @@ -187,7 +187,8 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCo syncProperty(pShape, RES_FRAMEDIR, 0, uno::makeAny(sal_Int16(eMode)), pObject); if (bIsGroupObj) - doTextBoxPositioning(pShape, pObject); + handleTextBoxGroup( + pShape, SwTextBoxHelper::GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE); // Check if the shape had text before and move it to the new textframe if (!bCopyText || sCopyableText.isEmpty()) @@ -207,18 +208,156 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCo } } +void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj, + uno::Reference<text::XTextFrame> xNew) +{ + // Do not set invalid data + assert(pShapeFormat && pObj && xNew); + // Firstly find the format of the new textbox. + SwFrameFormat* pFormat = nullptr; + if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get())) + pFormat = pTextFrame->GetFrameFormat(); + if (!pFormat) + return; + std::vector<std::pair<beans::Property, uno::Any>> aOldProps; + // If there is a format, check if the shape already has a textbox assigned to. + if (auto pTextBoxNode = pShapeFormat->GetOtherTextBoxFormat()) + { + // If it has a texbox, destroy it. + if (pTextBoxNode->GetTextBox(pObj)) + { + auto xOldFrame + = pObj->getUnoShape()->queryInterface(cppu::UnoType<text::XTextRange>::get()); + if (xOldFrame.hasValue()) + { + uno::Reference<beans::XPropertySet> xOldprops(xOldFrame, uno::UNO_QUERY); + uno::Reference<beans::XPropertyState> xOldPropStates(xOldFrame, uno::UNO_QUERY); + for (auto& rProp : xOldprops->getPropertySetInfo()->getProperties()) + { + try + { + if (xOldPropStates->getPropertyState(rProp.Name) + == beans::PropertyState::PropertyState_DIRECT_VALUE) + aOldProps.push_back( + std::pair(rProp, xOldprops->getPropertyValue(rProp.Name))); + } + catch (...) + { + } + } + } + destroy(pShapeFormat, pObj); + } + // And set the new one. + pTextBoxNode->AddTextBox(pObj, pFormat); + pFormat->SetOtherTextBoxFormat(pTextBoxNode); + } + else + { + // If the shape do not have a texbox node and textbox, + // create that for the shape. + auto* pTextBox = new SwTextBoxNode(pShapeFormat); + pTextBox->AddTextBox(pObj, pFormat); + pShapeFormat->SetOtherTextBoxFormat(pTextBox); + pFormat->SetOtherTextBoxFormat(pTextBox); + } + + // Initialize its properties + uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY); + uno::Any aEmptyBorder = uno::makeAny(table::BorderLine2()); + xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder); + xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder); + xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder); + xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder); + xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::makeAny(sal_Int32(100))); + xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::makeAny(text::SizeType::FIX)); + xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::makeAny(text::WrapTextMode_THROUGH)); + // Add a new name to it + uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY); + xNamed->setName(pShapeFormat->GetDoc()->GetUniqueFrameName()); + // And sync. properties. + uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY); + syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::makeAny(xShape->getSize()), + pObj); + + uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY); + syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, + xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObj); + syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT, + xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObj); + syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION, + xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj); + syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT, + xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObj); + syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION, + xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj); + syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION, + xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj); + syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION, + xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj); + syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, + xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj); + + drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER; + + if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW)) + ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST) + != beans::PropertyState::PropertyState_DEFAULT_VALUE) + { + aVertAdj = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST) + .get<drawing::TextVerticalAdjust>(); + } + + xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, uno::makeAny(aVertAdj)); + text::WritingMode eMode; + if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode) + syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::makeAny(sal_Int16(eMode)), pObj); + if (aOldProps.size()) + { + for (auto& rProp : aOldProps) + { + try + { + xPropertySet->setPropertyValue(rProp.first.Name, rProp.second); + } + catch (...) + { + } + } + } + if (pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE + && pFormat->GetAnchor().GetPageNum() == 0) + { + pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1)); + } + // Do sync for the new textframe. + handleTextBoxGroup(pShapeFormat, GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE); +} + void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject) { + assert(pShape && pObject); + // If a TextBox was enabled previously - auto pTextBox = pShape->GetOtherTextBoxFormat(); - if (pTextBox && pTextBox->IsTextBoxActive(pObject)) + auto pTextBoxNode = pShape->GetOtherTextBoxFormat(); + if (!pTextBoxNode) + { + SAL_WARN("sw.core", "SwTextBoxHelper::destroy: No TextBoxNode!"); + return; + } + + if (auto pFormat = pTextBoxNode->GetTextBox(pObject)) { // Unlink the TextBox's text range from the original shape. - pTextBox->SetTextBoxInactive(pObject); + pTextBoxNode->DelTextBox(pObject); + pFormat->SetOtherTextBoxFormat(nullptr); // Delete the associated TextFrame. - pTextBox->DelTextBox(pObject); + pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(pFormat); + return; } + + SAL_WARN("sw.core", "SwTextBoxHelper::destroy: No TextBox to destroy!"); } bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType, @@ -233,6 +372,9 @@ bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType, if (!pTextBox) return false; + if (!pTextBox->GetTextBoxCount()) + return false; + if (nType == RES_DRAWFRMFMT) { if (pObject) @@ -333,6 +475,12 @@ sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject) void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough) { + if (!pTextBox->GetOtherTextBoxFormat()) + { + SAL_WARN("sw.core", "SwTextBoxHelper::getShapeWrapThrough: No TextBoxNode!"); + return; + } + SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT); if (pShape) rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH; @@ -367,7 +515,8 @@ SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XS return nullptr; SwFrameFormat* pFormat = pShape->GetFrameFormat(); - return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT); + return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, + SdrObject::getSdrObjectFromXShape(xShape)); } uno::Reference<text::XTextFrame> @@ -388,33 +537,49 @@ SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape) return {}; } -template <typename T> static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny) +uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType, + SdrObject* pObj) { - if (SwFrameFormat* pFormat = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + if (!pShape) + return {}; + + if (!pShape->GetOtherTextBoxFormat()) { - uno::Reference<T> const xInterface( - SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY); - rAny <<= xInterface; + SAL_WARN("sw.core", "SwTextBoxHelper::queryInterface: No TextBoxNode!"); + return {}; + } + + SwFrameFormat* pFormat = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj); + + if (!pFormat) + { + SAL_WARN("sw.core", "SwTextBoxHelper::queryInterface: No TextBox!"); + return {}; } -} -uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType) -{ uno::Any aRet; if (rType == cppu::UnoType<css::text::XTextAppend>::get()) { - lcl_queryInterface<text::XTextAppend>(pShape, aRet); + uno::Reference<css::text::XTextAppend> const xInterface( + SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY_THROW); + aRet <<= xInterface; } else if (rType == cppu::UnoType<css::text::XText>::get()) { - lcl_queryInterface<text::XText>(pShape, aRet); + uno::Reference<css::text::XText> const xInterface( + SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY_THROW); + aRet <<= xInterface; } else if (rType == cppu::UnoType<css::text::XTextRange>::get()) { - lcl_queryInterface<text::XTextRange>(pShape, aRet); + uno::Reference<css::text::XTextRange> const xInterface( + SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY_THROW); + aRet <<= xInterface; } + assert(aRet.hasValue()); + return aRet; } @@ -908,6 +1073,51 @@ void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrame } } +void SwTextBoxHelper::handleTextBoxGroup(SwFrameFormat* pGroupShapeFormat, + GroupTextBoxActionType eActionType, + SdrObject* pGroupObject) +{ + if (!pGroupShapeFormat) + return; + + SdrObject* pMasterObj = nullptr; + if (!pGroupObject) + pMasterObj = pGroupShapeFormat->FindRealSdrObject(); + else + pMasterObj = pGroupObject; + + if (auto pChildrenObjs = pMasterObj->getChildrenOfSdrObject()) + { + for (size_t i = 0; i < pChildrenObjs->GetObjCount(); ++i) + handleTextBoxGroup(pGroupShapeFormat, eActionType, pChildrenObjs->GetObj(i)); + } + else + { + switch (eActionType) + { + case GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE: + { + changeAnchor(pGroupShapeFormat, pMasterObj); + setTextBoxSize(pGroupShapeFormat, pMasterObj); + break; + } + case GroupTextBoxActionType::Z_ORDER_CHANGE: + { + DoTextBoxZOrderCorrection(pGroupShapeFormat, pMasterObj); + break; + } + case GroupTextBoxActionType::DELETE: + { + destroy(pGroupShapeFormat, pMasterObj); + break; + } + default: + SAL_WARN("sw.core", "SwTextBoxHelper::handleTextBoxGroup: Unknown Action!"); + break; + } + } +} + text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID) { text::TextContentAnchorType aAnchorType; @@ -1066,7 +1276,6 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& if (aTextBoxSet.Count()) pFormat->SetFormatAttr(aTextBoxSet); - //pFormat->GetDoc()->SetFlyFrameAttr(*pFormat, aTextBoxSet); DoTextBoxZOrderCorrection(&rShape, pObj); } @@ -1114,12 +1323,11 @@ void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj) DoTextBoxZOrderCorrection(pParentFormat, pObj); } -bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape) +bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape, SdrObject* pObj) { - OUString sErrMsg; - if (isTextBoxShapeHasValidTextFrame(pShape)) + if (isTextBoxShapeHasValidTextFrame(pShape, pObj)) { - if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj)) { ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo()); if (auto xFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat)) @@ -1132,53 +1340,77 @@ bool SwTextBoxHelper::setWrapThrough(SwFrameFormat* pShape) } catch (uno::Exception& e) { - sErrMsg = "Exception caught: " + e.Message; + SAL_WARN("sw.core", + "SwTextBoxHelper::setWrapThrough: Exception caught: " << e.Message); } else - sErrMsg = "No XTextFrame!"; + SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: No XTextFrame!"); } else - sErrMsg = "No Other TextBox Format!"; + SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: No Other TextBox Format!"); } else - sErrMsg = "Not a Valid TextBox object!"; + SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: Not a Valid TextBox object!"); - SAL_WARN("sw.core", "SwTextBoxHelper::setWrapThrough: " << sErrMsg); return false; } bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj) { + // Get the desired textbox from the shape if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj)) { + // Collect the anchors const SwFormatAnchor& rOldAnch = pFormat->GetAnchor(); const SwFormatAnchor& rNewAnch = pShape->GetAnchor(); + // Collect the anchor positions (in the text) const auto pOldCnt = rOldAnch.GetContentAnchor(); const auto pNewCnt = rNewAnch.GetContentAnchor(); + // Collect the relative anchor points const uno::Any aShapeHorRelOrient = uno::makeAny(pShape->GetHoriOrient().GetRelationOrient()); + const uno::Any aShapeVertRelOrient + = uno::makeAny(pShape->GetVertOrient().GetRelationOrient()); - if (isAnchorTypeDifferent(pShape) || (pObj && pObj != pShape->FindRealSdrObject())) + // Get the new page number. If this is 0 incrase it to 1! + // (0 is invalid page) + const sal_uInt16 nPageNum = rNewAnch.GetPageNum() ? rNewAnch.GetPageNum() : 1; + + // If anchor is different, sync needed + if (isAnchorTypeDifferent(pShape, pObj) || (pObj && pObj != pShape->FindRealSdrObject())) { try { + // Disable undo for the sync ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo()); + // Get the properties of the textframe uno::Reference<beans::XPropertySet> const xPropertySet( SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY); + + // Anchoring situations: + + // First: + // the old anchor is in the text (namely inline or at_char/para) and, + // the new one will be at_page if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && rNewAnch.GetPageNum()) { uno::Any aValue(text::TextContentAnchorType_AT_PAGE); xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient); + xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, + aShapeVertRelOrient); xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue); - xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO, - uno::Any(rNewAnch.GetPageNum())); + xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO, uno::Any(nPageNum)); } + // Second: + // The reverse of the previous one: Old one at page, new at-text. else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt) { + // If the new at-text anchor is inline, the textbox can not be inline too, + // so change it to at_char and make its position according to the shape. if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR) { uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER); @@ -1191,17 +1423,28 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj) aPos.SetAnchor(pNewCnt); pFormat->SetFormatAttr(aPos); } + // Else, copy the anchor of the shape to the frame. Correct pagenum if needed. else { uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId())); xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient); + xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, + aShapeVertRelOrient); xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue); + if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE + && !rNewAnch.GetPageNum()) + xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO, + uno::Any(sal_uInt16(1))); + pFormat->SetFormatAttr(rNewAnch); } } + // Third: + // At-text anchor changes to at-text. (Only the type changes) else { + // Special treatment for inline case. if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR) { uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER); @@ -1214,10 +1457,19 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj) aPos.SetAnchor(pNewCnt); pFormat->SetFormatAttr(aPos); } + // Else, just copy the anchor. else { xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient); + xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, + aShapeVertRelOrient); + + if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE + && !rNewAnch.GetPageNum()) + xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO, + uno::Any(sal_uInt16(1))); + pFormat->SetFormatAttr(pShape->GetAnchor()); } } @@ -1253,6 +1505,12 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb SwFormatVertOrient aNewVOri(pFormat->GetVertOrient()); aNewVOri.SetPos(aRect.Top() + pShape->GetVertOrient().GetPos()); + if (bIsGroupObj) + { + aNewHOri.SetPos(aNewHOri.GetPos() + pObj->GetRelativePos().getX()); + aNewVOri.SetPos(aNewVOri.GetPos() + pObj->GetRelativePos().getY()); + } + // tdf#140598: Do not apply wrong rectangle position. if (aRect.TopLeft() != Point(0, 0)) { @@ -1272,11 +1530,11 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb { SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient()); aNewHOri.SetPos( - (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos()) + ((bIsGroupObj && pObj) ? pObj->GetRelativePos().getX() : aNewHOri.GetPos()) + aRect.Left()); SwFormatVertOrient aNewVOri(pShape->GetVertOrient()); aNewVOri.SetPos( - (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos()) + ((bIsGroupObj && pObj) ? pObj->GetRelativePos().getY() : aNewVOri.GetPos()) + aRect.Top()); pFormat->SetFormatAttr(aNewHOri); @@ -1291,12 +1549,31 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb return false; } -std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(const SwFrameFormat* pShape) +bool SwTextBoxHelper::setTextBoxSize(const SwFrameFormat* pShape, SdrObject* pObj) +{ + if (!pShape || !pObj) + return false; + + if (auto pTBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj)) + { + const auto& rSize = getTextRectangle(pObj, false).GetSize(); + if (!rSize.IsEmpty()) + { + SwFormatFrameSize aSizeFormatItem = pTBox->GetFrameSize(); + aSizeFormatItem.SetSize(rSize); + return pTBox->SetFormatAttr(aSizeFormatItem); + } + } + return false; +} + +std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(const SwFrameFormat* pShape, + SdrObject* pObj) { std::optional<bool> bRet; - if (isTextBoxShapeHasValidTextFrame(pShape)) + if (isTextBoxShapeHasValidTextFrame(pShape, pObj)) { - if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj)) { if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) bRet = (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_CHAR @@ -1308,20 +1585,21 @@ std::optional<bool> SwTextBoxHelper::isAnchorTypeDifferent(const SwFrameFormat* return bRet; } -bool SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape) +bool SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(const SwFrameFormat* pShape, SdrObject* pObj) { if (pShape && pShape->Which() == RES_DRAWFRMFMT) - if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT)) + if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj)) if (pFormat && pFormat->Which() == RES_FLYFRMFMT) return true; else - SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: " - "Shape does not have valid textframe!"); + SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Shape does " + "not have valid textframe!"); else - SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: " - "Shape does not have associated frame!"); + SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Shape does not " + "have associated frame!"); else - SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: Not valid shape!"); + SAL_WARN("sw.core", "SwTextBoxHelper::isTextBoxShapeHasValidTextFrame: No valid shape"); + return false; } @@ -1329,9 +1607,6 @@ bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr { // TODO: do this with group shape textboxes. SdrObject* pShpObj = nullptr; - //if (pObj) - // pShpObj = pObj; - //else pShpObj = pShape->FindRealSdrObject(); if (pShpObj) @@ -1365,39 +1640,53 @@ bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr break; ++nIterator; if (nIterator > 300) + { break; // Do not run to infinity + } } 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 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 frame!"); } - SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): " - "No Valid SdrObject for the shape!"); + SAL_WARN("sw.core", + "SwTextBoxHelper::DoTextBoxZOrderCorrection(): No Valid SdrObject for the shape!"); return false; } +// SwTextBoxNode class: + SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape) { assert(pOwnerShape); assert(pOwnerShape->Which() == RES_DRAWFRMFMT); m_pOwnerShapeFormat = pOwnerShape; - if (!m_pTextBoxes.empty()) - m_pTextBoxes.clear(); + if (!m_pTextBoxTable.empty()) + m_pTextBoxTable.clear(); } SwTextBoxNode::~SwTextBoxNode() { - m_pTextBoxes.clear(); + if (m_pTextBoxTable.size()) + { + for (auto& rTextBox : m_pTextBoxTable) + { + rTextBox.second->SetOtherTextBoxFormat(nullptr); + rTextBox.second = nullptr; + } + m_pTextBoxTable.clear(); + } if (m_pOwnerShapeFormat && m_pOwnerShapeFormat->GetOtherTextBoxFormat()) m_pOwnerShapeFormat->SetOtherTextBoxFormat(nullptr); + + m_pOwnerShapeFormat = nullptr; } void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox) @@ -1407,102 +1696,63 @@ void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBo assert(pDrawObject); - SwTextBoxElement aElem; - aElem.m_bIsActive = true; - aElem.m_pDrawObject = pDrawObject; - aElem.m_pTextBoxFormat = pNewTextBox; + if (!m_pTextBoxTable.count(pDrawObject)) + { + m_pTextBoxTable.emplace(pDrawObject, pNewTextBox); + } + else + { + m_pTextBoxTable[pDrawObject] = pNewTextBox; + } auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject); if (pSwFlyDraw) { pSwFlyDraw->SetTextBox(true); } - m_pTextBoxes.push_back(aElem); } void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject) { assert(pDrawObject); - if (m_pTextBoxes.empty()) - return; - - for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();) - { - if (it->m_pDrawObject == pDrawObject) - { - m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( - it->m_pTextBoxFormat); - it = m_pTextBoxes.erase(it); - break; - } - ++it; - } -} + auto pObj = const_cast<SdrObject*>(pDrawObject); -SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const -{ - assert(pDrawObject); - if (!m_pTextBoxes.empty()) - { - for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++) - { - if (it->m_pDrawObject == pDrawObject) - { - return it->m_pTextBoxFormat; - } - } - } - return nullptr; -} - -bool SwTextBoxNode::IsTextBoxActive(const SdrObject* pDrawObject) const -{ - assert(pDrawObject); + if (m_pTextBoxTable.empty()) + return; - if (!m_pTextBoxes.empty()) + if (m_pTextBoxTable.count(pObj)) { - for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++) - { - if (it->m_pDrawObject == pDrawObject) - { - return it->m_bIsActive; - } - } + m_pTextBoxTable[pObj]->SetOtherTextBoxFormat(nullptr); + m_pTextBoxTable.erase(pObj); } - return false; } -void SwTextBoxNode::SetTextBoxActive(const SdrObject* pDrawObject) +void SwTextBoxNode::DelTextBox(SwFrameFormat* pTextBox) { - assert(pDrawObject); + if (!pTextBox || m_pTextBoxTable.empty()) + return; - if (!m_pTextBoxes.empty()) - { - for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++) + for (auto it = m_pTextBoxTable.begin(); it != m_pTextBoxTable.end(); ++it) + if (it->second == pTextBox) { - if (it->m_pDrawObject == pDrawObject) - { - it->m_bIsActive = true; - } + it->second->SetOtherTextBoxFormat(nullptr); + m_pTextBoxTable.erase(it->first); + break; } - } } -void SwTextBoxNode::SetTextBoxInactive(const SdrObject* pDrawObject) +SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const { assert(pDrawObject); - - if (!m_pTextBoxes.empty()) + if (!m_pTextBoxTable.empty()) { - for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++) + if (m_pTextBoxTable.count(pDrawObject)) { - if (it->m_pDrawObject == pDrawObject) - { - it->m_bIsActive = false; - } + return m_pTextBoxTable.at(pDrawObject); } } + return nullptr; } -bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; } +bool SwTextBoxNode::IsGroupTextBoxShape() const { return m_pTextBoxTable.size() > 1; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx index cb9618c44e8b..f8aefc6acdd6 100644 --- a/sw/source/core/draw/dcontact.cxx +++ b/sw/source/core/draw/dcontact.cxx @@ -1250,10 +1250,9 @@ void SwDrawContact::Changed_( const SdrObject& rObj, // use geometry of drawing object aObjRect = pGroupObj->GetSnapRect(); - for (size_t i = 0; i < pGroupObj->getChildrenOfSdrObject()->GetObjCount(); ++i ) - { - SwTextBoxHelper::doTextBoxPositioning(GetFormat(), pGroupObj->getChildrenOfSdrObject()->GetObj(i)); - } + SwTextBoxHelper::handleTextBoxGroup( + GetFormat(), + SwTextBoxHelper::GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE); } SwTwips nXPosDiff(0); @@ -1370,7 +1369,9 @@ void SwDrawContact::Changed_( const SdrObject& rObj, aSet.Put(aSyncSet); aSet.Put(pSdrObj->GetMergedItem(RES_FRM_SIZE)); SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSet, pSdrObj); - SwTextBoxHelper::changeAnchor(GetFormat(), pSdrObj); + SwTextBoxHelper::handleTextBoxGroup( + GetFormat(), + SwTextBoxHelper::GroupTextBoxActionType::POSITION_SIZE_AND_ANCHOR_CHANGE); } else SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSyncSet, GetFormat()->FindRealSdrObject()); diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx index 510addf10a9c..6f7251698af8 100644 --- a/sw/source/core/draw/dview.cxx +++ b/sw/source/core/draw/dview.cxx @@ -967,12 +967,10 @@ void SwDrawView::DeleteMarked() SdrObject *pObject = rMarkList.GetMark(i)->GetMarkedSdrObj(); SwContact* pContact = GetUserCall(pObject); SwFrameFormat* pFormat = pContact->GetFormat(); - if (auto pChildren = pObject->getChildrenOfSdrObject()) + if (pObject->getChildrenOfSdrObject()) { - for (size_t it = 0; it < pChildren->GetObjCount(); ++it) - if (SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat( - pFormat, RES_DRAWFRMFMT, pChildren->GetObj(it))) - aTextBoxesToDelete.push_back(pTextBox); + SwTextBoxHelper::handleTextBoxGroup(pFormat, + SwTextBoxHelper::GroupTextBoxActionType::DELETE); } else if (SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT)) diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx index 1e3c8f2ff150..893926456d10 100644 --- a/sw/source/core/frmedt/feshview.cxx +++ b/sw/source/core/frmedt/feshview.cxx @@ -1064,7 +1064,7 @@ void SwFEShell::SelectionToTop( bool bTop ) if (auto pFormat = FindFrameFormat(pObj)) { // If it has not textframe skip... - if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat)) + if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat, pObj)) continue; // If it has a textframe so it is a textbox, get its page if (auto pDrwModel @@ -1133,7 +1133,7 @@ void SwFEShell::SelectionToBottom( bool bBottom ) if (auto pFormat = FindFrameFormat(pObj)) { // If the shape has not textframes skip. - if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat)) + if (!SwTextBoxHelper::isTextBoxShapeHasValidTextFrame(pFormat, pObj)) continue; // If has, move the shape to correct level with... if (auto pDrwModel diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx index 4791fda80169..9a2fb39ba680 100644 --- a/sw/source/core/layout/atrfrm.cxx +++ b/sw/source/core/layout/atrfrm.cxx @@ -2550,12 +2550,11 @@ SwFrameFormat::~SwFrameFormat() if( nullptr == m_pOtherTextBoxFormat ) return; - auto pObj = FindRealSdrObject(); - if (Which() == RES_FLYFRMFMT && pObj) + if (Which() == RES_FLYFRMFMT) { // This is a fly-frame-format just delete this // textbox entry from the draw-frame-format. - m_pOtherTextBoxFormat->DelTextBox(pObj); + m_pOtherTextBoxFormat->DelTextBox(this); } if (Which() == RES_DRAWFRMFMT) diff --git a/sw/source/core/unocore/unodraw.cxx b/sw/source/core/unocore/unodraw.cxx index 4d04857e7199..cbb0266c0af6 100644 --- a/sw/source/core/unocore/unodraw.cxx +++ b/sw/source/core/unocore/unodraw.cxx @@ -963,7 +963,13 @@ SwXShape::~SwXShape() uno::Any SwXShape::queryInterface( const uno::Type& aType ) { - uno::Any aRet = SwTextBoxHelper::queryInterface(GetFrameFormat(), aType); + uno::Any aRet; + if (aType == cppu::UnoType<text::XText>::get() + || aType == cppu::UnoType<text::XTextAppend>::get() + || aType == cppu::UnoType<text::XTextRange>::get()) + aRet = SwTextBoxHelper::queryInterface(GetFrameFormat(), aType, + SdrObject::getSdrObjectFromXShape(mxShape)); + if (aRet.hasValue()) return aRet; @@ -1151,18 +1157,28 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a } else if (pEntry->nWID == FN_TEXT_BOX) { - bool bValue(false); - aValue >>= bValue; - if (bValue) - SwTextBoxHelper::create(pFormat, GetSvxShape()->GetSdrObject()); - else - SwTextBoxHelper::destroy(pFormat, GetSvxShape()->GetSdrObject()); - + if (pEntry->nMemberId == MID_TEXTBOX) + { + bool bValue(false); + aValue >>= bValue; + if (bValue) + SwTextBoxHelper::create(pFormat, GetSvxShape()->GetSdrObject()); + else + SwTextBoxHelper::destroy(pFormat, GetSvxShape()->GetSdrObject()); + } + else if (pEntry->nMemberId == MID_TEXTBOX_CONTENT) + { + if (aValue.getValueType() == cppu::UnoType<uno::Reference<text::XTextFrame>>::get()) + SwTextBoxHelper::set(pFormat, GetSvxShape()->GetSdrObject(), + aValue.get<uno::Reference<text::XTextFrame>>()); + else + SAL_WARN( "sw.uno", "This is not a TextFrame!" ); + } } else if (pEntry->nWID == RES_CHAIN) { if (pEntry->nMemberId == MID_CHAIN_NEXTNAME || pEntry->nMemberId == MID_CHAIN_PREVNAME) - SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue); + SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue, SdrObject::getSdrObjectFromXShape(mxShape)); } // #i28749# else if ( FN_SHAPE_POSITION_LAYOUT_DIR == pEntry->nWID ) @@ -1333,7 +1349,7 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a pFormat->SetFormatAttr(aSet); } // We have a pFormat and a pEntry as well: try to sync TextBox property. - SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue); + SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue, SdrObject::getSdrObjectFromXShape(mxShape)); } else { @@ -1423,7 +1439,7 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a if (pFormat) { // We have a pFormat (but no pEntry): try to sync TextBox property. - SwTextBoxHelper::syncProperty(pFormat, rPropertyName, aValue); + SwTextBoxHelper::syncProperty(pFormat, rPropertyName, aValue, SdrObject::getSdrObjectFromXShape(mxShape)); } // #i31698# - restore object position, if caption point is set. @@ -1504,12 +1520,26 @@ uno::Any SwXShape::getPropertyValue(const OUString& rPropertyName) } else if (pEntry->nWID == FN_TEXT_BOX) { - auto pSvxShape = GetSvxShape(); - bool bValue = SwTextBoxHelper::isTextBox( - pFormat, RES_DRAWFRMFMT, - ((pSvxShape && pSvxShape->GetSdrObject()) ? pSvxShape->GetSdrObject() - : pFormat->FindRealSdrObject())); - aRet <<= bValue; + if (pEntry->nMemberId == MID_TEXTBOX) + { + auto pSvxShape = GetSvxShape(); + bool bValue = SwTextBoxHelper::isTextBox( + pFormat, RES_DRAWFRMFMT, + ((pSvxShape && pSvxShape->GetSdrObject()) ? pSvxShape->GetSdrObject() + : pFormat->FindRealSdrObject())); + aRet <<= bValue; + } + else if (pEntry->nMemberId == MID_TEXTBOX_CONTENT) + { + auto pObj = SdrObject::getSdrObjectFromXShape(mxShape); + auto xRange = SwTextBoxHelper::queryInterface( + pFormat, cppu::UnoType<text::XText>::get(), + pObj ? pObj : pFormat->FindRealSdrObject()); + + uno::Reference<text::XTextFrame> xFrame(xRange, uno::UNO_QUERY); + if (xFrame.is()) + aRet <<= xFrame; + } } else if (pEntry->nWID == RES_CHAIN) { @@ -1790,7 +1820,7 @@ uno::Sequence< beans::PropertyState > SwXShape::getPropertyStates( else if (pEntry->nWID == FN_TEXT_BOX) { // The TextBox property is set, if we can find a textbox for this shape. - if (pFormat && SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT)) + if (pFormat && SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, SdrObject::getSdrObjectFromXShape(mxShape))) pRet[nProperty] = beans::PropertyState_DIRECT_VALUE; else pRet[nProperty] = beans::PropertyState_DEFAULT_VALUE; diff --git a/sw/source/core/unocore/unomap.cxx b/sw/source/core/unocore/unomap.cxx index 4e373b6c511e..3b9f0d7d8944 100644 --- a/sw/source/core/unocore/unomap.cxx +++ b/sw/source/core/unocore/unomap.cxx @@ -291,7 +291,8 @@ const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetPropertyMapEntries(s { u"" UNO_NAME_RELATIVE_HEIGHT_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION }, { u"" UNO_NAME_RELATIVE_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH }, { u"" UNO_NAME_RELATIVE_WIDTH_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION }, - { u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, + { u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TEXTBOX}, + { u"" UNO_NAME_TEXT_BOX_CONTENT, FN_TEXT_BOX, cppu::UnoType<text::XTextFrame>::get(), PROPERTY_NONE, MID_TEXTBOX_CONTENT}, { u"" UNO_NAME_CHAIN_NEXT_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NEXTNAME}, { u"" UNO_NAME_CHAIN_PREV_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_PREVNAME}, { u"" UNO_NAME_CHAIN_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NAME }, diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx index 9a186b05d280..63b4f2d5e209 100644 --- a/sw/source/core/unocore/unomap1.cxx +++ b/sw/source/core/unocore/unomap1.cxx @@ -786,6 +786,7 @@ const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetFramePropertyMap() { u"" UNO_NAME_SIZE_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE }, { u"" UNO_NAME_WIDTH_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE }, { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, + //{ u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TEXTBOX}, // added FillProperties for SW, same as FILL_PROPERTIES in svx // but need own defines in Writer due to later association of strings