sw/Library_sw.mk | 1 sw/inc/formatcontentcontrol.hxx | 104 +++++++ sw/inc/hintids.hxx | 17 - sw/inc/textcontentcontrol.hxx | 41 ++ sw/source/core/bastyp/init.cxx | 5 sw/source/core/doc/DocumentContentOperationsManager.cxx | 2 sw/source/core/doc/dbgoutsw.cxx | 2 sw/source/core/txtnode/attrcontentcontrol.cxx | 237 ++++++++++++++++ sw/source/core/txtnode/thints.cxx | 32 +- sw/source/core/txtnode/txatbase.cxx | 2 sw/source/filter/html/css1atr.cxx | 2 sw/source/filter/html/htmlatr.cxx | 2 12 files changed, 429 insertions(+), 18 deletions(-)
New commits: commit 3dd4f3691458ea537bc1867386269694775cfbcb Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Wed Mar 30 08:16:21 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Mar 30 09:49:03 2022 +0200 sw content controls: add document model This is meant to represent inline structured document tags (<w:sdt> elements in DOCX) or content controls (as Word users know this). Don't confuse this with block-level, cell-level or table-level content controls, which are not covered here. You may wonder why fields or fieldmarks can't be used to represent this. The problems are: - a fieldmark can contain a paragraph break, inline content controls can't - content controls must be a well-formed XML element, while fieldmarks can start/end at random document model positions - fieldmarks are supposed to have a field command and a result result (with a separator between the two), but content controls don't have a field command - mapping to a field has the problem that Writer fields can only have a single text portion / run, but content controls can have multiple ones So model content controls with a meta-like text attribute which covers all these cases. SwContentControl is mostly empty as a start, it can track more data in follow-up commits. SwTextContentControl and SwFormatContentControl are the matching text attr and pool item subclasses. Change-Id: I1e57f7756cf87041975371483ec4fc8abf875dfe Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132291 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk index 30c53b6131b1..c63efce27993 100644 --- a/sw/Library_sw.mk +++ b/sw/Library_sw.mk @@ -427,6 +427,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\ sw/source/core/tox/ToxTextGenerator \ sw/source/core/tox/ToxWhitespaceStripper \ sw/source/core/txtnode/SwGrammarContact \ + sw/source/core/txtnode/attrcontentcontrol \ sw/source/core/txtnode/atrfld \ sw/source/core/txtnode/atrflyin \ sw/source/core/txtnode/atrftn \ diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx new file mode 100644 index 000000000000..0fa075bf7036 --- /dev/null +++ b/sw/inc/formatcontentcontrol.hxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/text/XTextContent.hpp> + +#include <cppuhelper/weakref.hxx> +#include <svl/poolitem.hxx> + +#include "calbck.hxx" + +class SwContentControl; +class SwTextContentControl; +class SwTextNode; +class SwXContentControl; + +/// SfxPoolItem subclass that wraps an SwContentControl. +class SwFormatContentControl final : public SfxPoolItem +{ + std::shared_ptr<SwContentControl> m_pContentControl; + SwTextContentControl* m_pTextAttr; + +public: + SwTextContentControl* GetTextAttr() { return m_pTextAttr; } + void SetTextAttr(SwTextContentControl* pTextAttr); + + /// This method must be called when the hint is actually copied. + void DoCopy(SwTextNode& rTargetTextNode); + + explicit SwFormatContentControl(sal_uInt16 nWhich); + + explicit SwFormatContentControl(const std::shared_ptr<SwContentControl>& pContentControl, + sal_uInt16 nWhich); + ~SwFormatContentControl() override; + + /// SfxPoolItem + bool operator==(const SfxPoolItem&) const override; + SwFormatContentControl* Clone(SfxItemPool* pPool = nullptr) const override; + + /** + * Notify clients registered at m_pContentControl that this content control is being + * (re-)moved. + */ + void NotifyChangeTextNode(SwTextNode* pTextNode); + static SwFormatContentControl* CreatePoolDefault(sal_uInt16 nWhich); + SwContentControl* GetContentControl() { return m_pContentControl.get(); } +}; + +/// Stores the properties of a content control. +class SwContentControl : public sw::BroadcastingModify +{ + css::uno::WeakReference<css::text::XTextContent> m_wXContentControl; + + SwFormatContentControl* m_pFormat; + + /// Can be nullptr if not in a document for undo purposes. + SwTextNode* m_pTextNode; + +public: + SwTextContentControl* GetTextAttr() const; + + SwTextNode* GetTextNode() const { return m_pTextNode; } + + SwFormatContentControl* GetFormatContentControl() const { return m_pFormat; } + + void SetFormatContentControl(SwFormatContentControl* pFormat) { m_pFormat = pFormat; }; + + void NotifyChangeTextNode(SwTextNode* pTextNode); + + css::uno::WeakReference<css::text::XTextContent> GetXContentControl() const + { + return m_wXContentControl; + } + + void SetXContentControl(const css::uno::Reference<css::text::XTextContent>& xContentCnotrol) + { + m_wXContentControl = xContentCnotrol; + } + + virtual void SwClientNotify(const SwModify&, const SfxHint&) override; + + explicit SwContentControl(SwFormatContentControl* pFormat); + + virtual ~SwContentControl() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/hintids.hxx b/sw/inc/hintids.hxx index 76068d21ec47..7c6a18723af2 100644 --- a/sw/inc/hintids.hxx +++ b/sw/inc/hintids.hxx @@ -33,6 +33,7 @@ class SwUpdateAttr; class SwAttrSetChg; class SwDocPosUpdate; class SwFormatMeta; +class SwFormatContentControl; class SvXMLAttrContainerItem; class SwMsgPoolItem; class SwPtrMsgPoolItem; @@ -276,17 +277,17 @@ constexpr TypedWhichId<SwFormatCharFormat> RES_TXTATR_CHARFMT(52); constexpr TypedWhichId<SwFormatRuby> RES_TXTATR_CJK_RUBY(53); constexpr TypedWhichId<SvXMLAttrContainerItem> RES_TXTATR_UNKNOWN_CONTAINER(54); constexpr TypedWhichId<SwFormatField> RES_TXTATR_INPUTFIELD(55); -constexpr sal_uInt16 RES_TXTATR_WITHEND_END(56); +constexpr TypedWhichId<SwFormatContentControl> RES_TXTATR_CONTENTCONTROL(56); +constexpr sal_uInt16 RES_TXTATR_WITHEND_END(57); // all TextAttributes without an end constexpr sal_uInt16 RES_TXTATR_NOEND_BEGIN(RES_TXTATR_WITHEND_END); -constexpr TypedWhichId<SwFormatField> RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN); // 56 -constexpr TypedWhichId<SwFormatFlyCnt> RES_TXTATR_FLYCNT(57); -constexpr TypedWhichId<SwFormatFootnote> RES_TXTATR_FTN(58); -constexpr TypedWhichId<SwFormatField> RES_TXTATR_ANNOTATION(59); -constexpr TypedWhichId<SwFormatLineBreak> RES_TXTATR_LINEBREAK(60); -constexpr TypedWhichId<SfxBoolItem> RES_TXTATR_DUMMY1(61); -constexpr TypedWhichId<SfxBoolItem> RES_TXTATR_DUMMY2(62); +constexpr TypedWhichId<SwFormatField> RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN); // 57 +constexpr TypedWhichId<SwFormatFlyCnt> RES_TXTATR_FLYCNT(58); +constexpr TypedWhichId<SwFormatFootnote> RES_TXTATR_FTN(59); +constexpr TypedWhichId<SwFormatField> RES_TXTATR_ANNOTATION(60); +constexpr TypedWhichId<SwFormatLineBreak> RES_TXTATR_LINEBREAK(61); +constexpr TypedWhichId<SfxBoolItem> RES_TXTATR_DUMMY1(62); constexpr sal_uInt16 RES_TXTATR_NOEND_END(63); constexpr sal_uInt16 RES_TXTATR_END(RES_TXTATR_NOEND_END); diff --git a/sw/inc/textcontentcontrol.hxx b/sw/inc/textcontentcontrol.hxx new file mode 100644 index 000000000000..3410a6a35506 --- /dev/null +++ b/sw/inc/textcontentcontrol.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include "txatbase.hxx" + +class SwFormatContentControl; + +/// SwTextAttr subclass that tracks the location of the wrapped SwFormatContentControl. +class SW_DLLPUBLIC SwTextContentControl final : public SwTextAttrNesting +{ + SwTextContentControl(SwFormatContentControl& rAttr, sal_Int32 nStart, sal_Int32 nEnd); + +public: + static SwTextContentControl* CreateTextContentControl(SwTextNode* pTargetTextNode, + SwFormatContentControl& rAttr, + sal_Int32 nStart, sal_Int32 nEnd, + bool bIsCopy); + + ~SwTextContentControl() override; + + void ChgTextNode(SwTextNode* pNode); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx index f7ad215602bf..461d7bc2da6e 100644 --- a/sw/source/core/bastyp/init.cxx +++ b/sw/source/core/bastyp/init.cxx @@ -100,6 +100,7 @@ #include <fmtline.hxx> #include <fmtlsplt.hxx> #include <fmtmeta.hxx> +#include <formatcontentcontrol.hxx> #include <fmtornt.hxx> #include <fmtpdsc.hxx> #include <fmtrfmrk.hxx> @@ -315,6 +316,7 @@ SfxItemInfo aSlotTab[] = { SID_ATTR_CHAR_CJK_RUBY, false }, // RES_TXTATR_CJK_RUBY { 0, true }, // RES_TXTATR_UNKNOWN_CONTAINER { 0, false }, // RES_TXTATR_INPUTFIELD + { 0, false }, // RES_TXTATR_CONTENTCONTROL { 0, false }, // RES_TXTATR_FIELD { 0, false }, // RES_TXTATR_FLYCNT @@ -322,7 +324,6 @@ SfxItemInfo aSlotTab[] = { 0, false }, // RES_TXTATR_ANNOTATION { 0, false }, // RES_TXTATR_LINEBREAK { 0, true }, // RES_TXTATR_DUMMY1 - { 0, true }, // RES_TXTATR_DUMMY2 { SID_ATTR_PARA_LINESPACE, true }, // RES_PARATR_LINESPACING { SID_ATTR_PARA_ADJUST, true }, // RES_PARATR_ADJUST @@ -513,6 +514,7 @@ void InitCore() aAttrTab[ RES_TXTATR_CJK_RUBY - POOLATTR_BEGIN ] = new SwFormatRuby( OUString() ); aAttrTab[ RES_TXTATR_UNKNOWN_CONTAINER - POOLATTR_BEGIN ] = new SvXMLAttrContainerItem( RES_TXTATR_UNKNOWN_CONTAINER ); aAttrTab[ RES_TXTATR_INPUTFIELD - POOLATTR_BEGIN ] = new SwFormatField( RES_TXTATR_INPUTFIELD ); + aAttrTab[ RES_TXTATR_CONTENTCONTROL - POOLATTR_BEGIN ] = new SwFormatContentControl( RES_TXTATR_CONTENTCONTROL ); aAttrTab[ RES_TXTATR_FIELD- POOLATTR_BEGIN ] = new SwFormatField( RES_TXTATR_FIELD ); aAttrTab[ RES_TXTATR_FLYCNT - POOLATTR_BEGIN ] = new SwFormatFlyCnt( nullptr ); @@ -522,7 +524,6 @@ void InitCore() // TextAttr - Dummies aAttrTab[ RES_TXTATR_DUMMY1 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_TXTATR_DUMMY1 ); - aAttrTab[ RES_TXTATR_DUMMY2 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_TXTATR_DUMMY2 ); aAttrTab[ RES_PARATR_LINESPACING- POOLATTR_BEGIN ] = new SvxLineSpacingItem( LINE_SPACE_DEFAULT_HEIGHT, RES_PARATR_LINESPACING ); aAttrTab[ RES_PARATR_ADJUST- POOLATTR_BEGIN ] = new SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ); diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx index ca26163d3d90..8eb9f3fc4864 100644 --- a/sw/source/core/doc/DocumentContentOperationsManager.cxx +++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx @@ -1563,7 +1563,7 @@ namespace //local functions originally from docfmt.cxx SfxItemSetFixed< RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD, RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY, - RES_TXTATR_INPUTFIELD, RES_TXTATR_INPUTFIELD> + RES_TXTATR_INPUTFIELD, RES_TXTATR_CONTENTCONTROL> aTextSet(rDoc.GetAttrPool()); aTextSet.Put( rChgSet ); diff --git a/sw/source/core/doc/dbgoutsw.cxx b/sw/source/core/doc/dbgoutsw.cxx index ced0488228e4..20d6828f81a0 100644 --- a/sw/source/core/doc/dbgoutsw.cxx +++ b/sw/source/core/doc/dbgoutsw.cxx @@ -138,6 +138,7 @@ static std::map<sal_uInt16,OUString> & GetItemWhichMap() { RES_TXTATR_TOXMARK , "TXTATR_TOXMARK" }, { RES_TXTATR_CHARFMT , "TXTATR_CHARFMT" }, { RES_TXTATR_INPUTFIELD , "RES_TXTATR_INPUTFIELD" }, + { RES_TXTATR_CONTENTCONTROL , "RES_TXTATR_CONTENTCONTROL" }, { RES_TXTATR_CJK_RUBY , "TXTATR_CJK_RUBY" }, { RES_TXTATR_UNKNOWN_CONTAINER , "TXTATR_UNKNOWN_CONTAINER" }, { RES_TXTATR_META , "TXTATR_META" }, @@ -148,7 +149,6 @@ static std::map<sal_uInt16,OUString> & GetItemWhichMap() { RES_TXTATR_ANNOTATION , "TXTATR_ANNOTATION" }, { RES_TXTATR_LINEBREAK , "RES_TXTATR_LINEBREAK" }, { RES_TXTATR_DUMMY1 , "TXTATR_DUMMY1" }, - { RES_TXTATR_DUMMY2 , "TXTATR_DUMMY2" }, { RES_PARATR_LINESPACING , "PARATR_LINESPACING" }, { RES_PARATR_ADJUST , "PARATR_ADJUST" }, { RES_PARATR_SPLIT , "PARATR_SPLIT" }, diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx new file mode 100644 index 000000000000..eb127f1a1ba6 --- /dev/null +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <formatcontentcontrol.hxx> + +#include <sal/log.hxx> + +#include <ndtxt.hxx> +#include <textcontentcontrol.hxx> + +using namespace com::sun::star; + +SwFormatContentControl* SwFormatContentControl::CreatePoolDefault(sal_uInt16 nWhich) +{ + return new SwFormatContentControl(nWhich); +} + +SwFormatContentControl::SwFormatContentControl(sal_uInt16 nWhich) + : SfxPoolItem(nWhich) + , m_pTextAttr(nullptr) +{ +} + +SwFormatContentControl::SwFormatContentControl( + const std::shared_ptr<SwContentControl>& pContentControl, sal_uInt16 nWhich) + : SfxPoolItem(nWhich) + , m_pContentControl(pContentControl) + , m_pTextAttr(nullptr) +{ + if (!pContentControl) + { + SAL_WARN("sw.core", "SwFormatContentControl ctor: no pContentControl?"); + } + // Not calling m_pContentControl->SetFormatContentControl(this) here; only from SetTextAttr. +} + +SwFormatContentControl::~SwFormatContentControl() +{ + if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this)) + { + NotifyChangeTextNode(nullptr); + m_pContentControl->SetFormatContentControl(nullptr); + } +} + +bool SwFormatContentControl::operator==(const SfxPoolItem& rOther) const +{ + return SfxPoolItem::operator==(rOther) + && m_pContentControl + == static_cast<const SwFormatContentControl&>(rOther).m_pContentControl; +} + +SwFormatContentControl* SwFormatContentControl::Clone(SfxItemPool* /*pPool*/) const +{ + // If this is indeed a copy, then DoCopy will be called later. + if (m_pContentControl) + { + return new SwFormatContentControl(m_pContentControl, Which()); + } + else + { + return new SwFormatContentControl(Which()); + } +} + +void SwFormatContentControl::SetTextAttr(SwTextContentControl* pTextAttr) +{ + if (m_pTextAttr && pTextAttr) + { + SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: already has a text attribute"); + } + if (!m_pTextAttr && !pTextAttr) + { + SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: no attribute to remove"); + } + m_pTextAttr = pTextAttr; + if (!m_pContentControl) + { + SAL_WARN("sw.core", "inserted SwFormatContentControl has no SwContentControl"); + } + // The SwContentControl should be able to find the current text attribute. + if (m_pContentControl) + { + if (pTextAttr) + { + m_pContentControl->SetFormatContentControl(this); + } + else if (m_pContentControl->GetFormatContentControl() == this) + { + // The text attribute is gone, so de-register from text node. + NotifyChangeTextNode(nullptr); + m_pContentControl->SetFormatContentControl(nullptr); + } + } +} + +void SwFormatContentControl::NotifyChangeTextNode(SwTextNode* pTextNode) +{ + // Not deleting m_pTextAttr here, SwNodes::ChgNode() doesn't do that, either. + if (!m_pContentControl) + { + SAL_WARN("sw.core", "SwFormatContentControl::NotifyChangeTextNode: no content control?"); + } + if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this)) + { + // Not calling Modify, that would call SwXContentControl::SwClientNotify. + m_pContentControl->NotifyChangeTextNode(pTextNode); + } +} + +// This SwFormatContentControl has been cloned and points at the same SwContentControl as the +// source: this function copies the SwContentControl. +void SwFormatContentControl::DoCopy(SwTextNode& rTargetTextNode) +{ + if (!m_pContentControl) + { + SAL_WARN("sw.core", "SwFormatContentControl::DoCopy: called for SwFormatContentControl " + "with no SwContentControl."); + return; + } + + m_pContentControl = std::make_shared<SwContentControl>(this); + m_pContentControl->NotifyChangeTextNode(&rTargetTextNode); +} + +SwContentControl::SwContentControl(SwFormatContentControl* pFormat) + : sw::BroadcastingModify() + , m_pFormat(pFormat) + , m_pTextNode(nullptr) +{ +} + +SwContentControl::~SwContentControl() {} + +SwTextContentControl* SwContentControl::GetTextAttr() const +{ + return m_pFormat ? m_pFormat->GetTextAttr() : nullptr; +} + +void SwContentControl::NotifyChangeTextNode(SwTextNode* pTextNode) +{ + m_pTextNode = pTextNode; + if (m_pTextNode && (GetRegisteredIn() != m_pTextNode)) + { + m_pTextNode->Add(this); + } + else if (!m_pTextNode) + { + EndListeningAll(); + } + if (!pTextNode) + { + // If the text node is gone, then invalidate clients (e.g. UNO object). + GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing)); + } +} + +void SwContentControl::SwClientNotify(const SwModify&, const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::SwLegacyModify) + return; + + auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); + CallSwClientNotify(rHint); + GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged)); + + if (pLegacy->GetWhich() == RES_REMOVE_UNO_OBJECT) + { + // Invalidate cached uno object. + SetXContentControl(uno::Reference<text::XTextContent>()); + GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing)); + } +} + +SwTextContentControl* SwTextContentControl::CreateTextContentControl(SwTextNode* pTargetTextNode, + SwFormatContentControl& rAttr, + sal_Int32 nStart, + sal_Int32 nEnd, bool bIsCopy) +{ + if (bIsCopy) + { + // rAttr is already cloned, now call DoCopy to copy the SwContentControl + if (!pTargetTextNode) + { + SAL_WARN("sw.core", + "SwTextContentControl ctor: cannot copy content control without target node"); + } + rAttr.DoCopy(*pTargetTextNode); + } + auto pTextContentControl(new SwTextContentControl(rAttr, nStart, nEnd)); + return pTextContentControl; +} + +SwTextContentControl::SwTextContentControl(SwFormatContentControl& rAttr, sal_Int32 nStart, + sal_Int32 nEnd) + : SwTextAttr(rAttr, nStart) + , SwTextAttrNesting(rAttr, nStart, nEnd) +{ + rAttr.SetTextAttr(this); + SetHasDummyChar(true); +} + +SwTextContentControl::~SwTextContentControl() +{ + auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr()); + if (rFormatContentControl.GetTextAttr() == this) + { + rFormatContentControl.SetTextAttr(nullptr); + } +} + +void SwTextContentControl::ChgTextNode(SwTextNode* pNode) +{ + auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr()); + if (rFormatContentControl.GetTextAttr() == this) + { + rFormatContentControl.NotifyChangeTextNode(pNode); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx index b6ca240fcffd..b15667ec6fcc 100644 --- a/sw/source/core/txtnode/thints.cxx +++ b/sw/source/core/txtnode/thints.cxx @@ -52,6 +52,8 @@ #include <ftnidx.hxx> #include <fmtruby.hxx> #include <fmtmeta.hxx> +#include <formatcontentcontrol.hxx> +#include <textcontentcontrol.hxx> #include <breakit.hxx> #include <doc.hxx> #include <IDocumentUndoRedo.hxx> @@ -104,7 +106,8 @@ SwpHints::SwpHints(const SwTextNode& rParent) static void TextAttrDelete( SwDoc & rDoc, SwTextAttr * const pAttr ) { if (RES_TXTATR_META == pAttr->Which() || - RES_TXTATR_METAFIELD == pAttr->Which()) + RES_TXTATR_METAFIELD == pAttr->Which() || + pAttr->Which() == RES_TXTATR_CONTENTCONTROL) { static_txtattr_cast<SwTextMeta *>(pAttr)->ChgTextNode(nullptr); // prevents ASSERT } @@ -160,7 +163,8 @@ bool isSelfNestable(const sal_uInt16 nWhich) (RES_TXTATR_INPUTFIELD == nWhich)) return false; assert((RES_TXTATR_META == nWhich) || - (RES_TXTATR_METAFIELD == nWhich)); + (RES_TXTATR_METAFIELD == nWhich) || + (RES_TXTATR_CONTENTCONTROL == nWhich)); return true; } @@ -172,7 +176,8 @@ bool isSplittable(const sal_uInt16 nWhich) return true; assert((RES_TXTATR_META == nWhich) || (RES_TXTATR_METAFIELD == nWhich) || - (RES_TXTATR_INPUTFIELD == nWhich)); + (RES_TXTATR_INPUTFIELD == nWhich) || + (RES_TXTATR_CONTENTCONTROL == nWhich)); return false; } @@ -299,7 +304,8 @@ void SwpHints::InsertNesting(SwTextAttrNesting & rNewHint) /** The following hints correspond to well-formed XML elements in ODF: -RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD +RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD, +RES_TXTATR_CONTENTCONTROL The writer core must ensure that these do not overlap; if they did, the document would not be storable as ODF. @@ -323,6 +329,8 @@ the result is properly nested. meta and meta-field must not be split, because they have xml:id. +content controls should not split, either. + These constraints result in the following design: RES_TXTATR_INETFMT: @@ -376,6 +384,7 @@ SwpHints::TryInsertNesting( SwTextNode & rNode, SwTextAttrNesting & rNewHint ) (RES_TXTATR_CJK_RUBY == nNewWhich) || (RES_TXTATR_META == nNewWhich) || (RES_TXTATR_METAFIELD == nNewWhich) || + (RES_TXTATR_CONTENTCONTROL == nNewWhich) || (RES_TXTATR_INPUTFIELD == nNewWhich)); NestList_t OverlappingExisting; // existing hints to be split @@ -1143,6 +1152,11 @@ SwTextAttr* MakeTextAttr( case RES_TXTATR_LINEBREAK: pNew = new SwTextLineBreak(static_cast<SwFormatLineBreak&>(rNew), nStt); break; + case RES_TXTATR_CONTENTCONTROL: + pNew = SwTextContentControl::CreateTextContentControl( + pTextNode, static_cast<SwFormatContentControl&>(rNew), nStt, nEnd, + bIsCopy == CopyOrNewType::Copy); + break; default: assert(RES_TXTATR_AUTOFMT == rNew.Which()); pNew = new SwTextAttrEnd( rNew, nStt, nEnd ); @@ -1270,6 +1284,11 @@ void SwTextNode::DestroyAttr( SwTextAttr* pAttr ) static_txtattr_cast<SwTextMeta*>(pAttr)->ChgTextNode(nullptr); } break; + case RES_TXTATR_CONTENTCONTROL: + { + static_txtattr_cast<SwTextContentControl*>(pAttr)->ChgTextNode(nullptr); + break; + } default: break; @@ -3164,6 +3183,10 @@ bool SwpHints::TryInsertHint( static_txtattr_cast<SwTextMeta *>(pHint)->ChgTextNode( &rNode ); break; + case RES_TXTATR_CONTENTCONTROL: + static_txtattr_cast<SwTextContentControl*>(pHint)->ChgTextNode( &rNode ); + break; + case RES_CHRATR_HIDDEN: rNode.SetCalcHiddenCharFlags(); break; @@ -3469,6 +3492,7 @@ sal_Unicode GetCharOfTextAttr( const SwTextAttr& rAttr ) case RES_TXTATR_FTN: case RES_TXTATR_META: case RES_TXTATR_METAFIELD: + case RES_TXTATR_CONTENTCONTROL: { cRet = CH_TXTATR_BREAKWORD; } diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx index d4235a830358..cd18549c58d8 100644 --- a/sw/source/core/txtnode/txatbase.cxx +++ b/sw/source/core/txtnode/txatbase.cxx @@ -165,6 +165,8 @@ void SwTextAttr::dumpAsXml(xmlTextWriterPtr pWriter) const break; case RES_TXTATR_META: break; + case RES_TXTATR_CONTENTCONTROL: + break; default: SAL_WARN("sw.core", "Unhandled TXTATR"); break; diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx index ef3274ac6faf..9e4de8c52527 100644 --- a/sw/source/filter/html/css1atr.cxx +++ b/sw/source/filter/html/css1atr.cxx @@ -3457,6 +3457,7 @@ SwAttrFnTab const aCSS1AttrFnTab = { /* RES_TXTATR_CJK_RUBY */ nullptr, /* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr, /* RES_TXTATR_INPUTFIELD */ nullptr, +/* RES_TXTATR_CONTENTCONTROL */ nullptr, /* RES_TXTATR_FIELD */ nullptr, /* RES_TXTATR_FLYCNT */ nullptr, @@ -3464,7 +3465,6 @@ SwAttrFnTab const aCSS1AttrFnTab = { /* RES_TXTATR_ANNOTATION */ nullptr, /* RES_TXTATR_LINEBREAK */ nullptr, /* RES_TXTATR_DUMMY1 */ nullptr, // Dummy: -/* RES_TXTATR_DUMMY2 */ nullptr, // Dummy: /* RES_PARATR_LINESPACING */ OutCSS1_SvxLineSpacing, /* RES_PARATR_ADJUST */ OutCSS1_SvxAdjust, diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index 7239bd1a8caa..874437ede6ec 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -3281,6 +3281,7 @@ SwAttrFnTab aHTMLAttrFnTab = { /* RES_TXTATR_CJK_RUBY */ nullptr, /* RES_TXTATR_UNKNOWN_CONTAINER */ nullptr, /* RES_TXTATR_INPUTFIELD */ OutHTML_SwFormatField, +/* RES_TXTATR_CONTENTCONTROL */ nullptr, /* RES_TXTATR_FIELD */ OutHTML_SwFormatField, /* RES_TXTATR_FLYCNT */ OutHTML_SwFlyCnt, @@ -3288,7 +3289,6 @@ SwAttrFnTab aHTMLAttrFnTab = { /* RES_TXTATR_ANNOTATION */ OutHTML_SwFormatField, /* RES_TXTATR_LINEBREAK */ OutHTML_SwFormatLineBreak, /* RES_TXTATR_DUMMY1 */ nullptr, // Dummy: -/* RES_TXTATR_DUMMY2 */ nullptr, // Dummy: /* RES_PARATR_LINESPACING */ nullptr, /* RES_PARATR_ADJUST */ OutHTML_SvxAdjust,