1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <sdr/properties/attributeproperties.hxx>
23 #include <tools/debug.hxx>
24 #include <svl/itemset.hxx>
25 #include <svl/style.hxx>
26 #include <svl/whiter.hxx>
27 #include <svl/poolitem.hxx>
28 #include <svx/svdobj.hxx>
29 #include <svx/svddef.hxx>
30 #include <svx/xbtmpit.hxx>
31 #include <svx/xlndsit.hxx>
32 #include <svx/xlnstit.hxx>
33 #include <svx/xlnedit.hxx>
34 #include <svx/xflgrit.hxx>
35 #include <svx/xflftrit.hxx>
36 #include <svx/xflhtit.hxx>
37 #include <svx/svdmodel.hxx>
38 #include <svx/svdpage.hxx>
39 #include <osl/diagnose.h>
41 namespace sdr::properties
43 void AttributeProperties::ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr
)
45 if(HasSfxItemSet() && mpStyleSheet
)
47 // Delete hard attributes where items are set in the style sheet
48 if(!bDontRemoveHardAttr
)
50 const SfxItemSet
& rStyle
= mpStyleSheet
->GetItemSet();
51 SfxWhichIter
aIter(rStyle
);
52 sal_uInt16 nWhich
= aIter
.FirstWhich();
56 if(SfxItemState::SET
== aIter
.GetItemState())
58 moItemSet
->ClearItem(nWhich
);
61 nWhich
= aIter
.NextWhich();
65 // set new stylesheet as parent
66 moItemSet
->SetParent(&mpStyleSheet
->GetItemSet());
70 OSL_ENSURE(false, "ImpSetParentAtSfxItemSet called without SfxItemSet/SfxStyleSheet (!)");
74 void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet
* pNewStyleSheet
, bool bDontRemoveHardAttr
)
76 // test if old StyleSheet is cleared, else it would be lost
77 // after this method -> memory leak (!)
78 DBG_ASSERT(!mpStyleSheet
, "Old style sheet not deleted before setting new one (!)");
84 mpStyleSheet
= pNewStyleSheet
;
88 // register as listener
89 StartListening(*pNewStyleSheet
->GetPool());
90 StartListening(*pNewStyleSheet
);
92 // only apply the following when we have an SfxItemSet already, else
95 ImpSetParentAtSfxItemSet(bDontRemoveHardAttr
);
100 void AttributeProperties::ImpRemoveStyleSheet()
102 // Check type since it is destroyed when the type is deleted
103 if(GetStyleSheet() && mpStyleSheet
)
105 EndListening(*mpStyleSheet
);
106 if (auto const pool
= mpStyleSheet
->GetPool()) { // TTTT
110 // reset parent of ItemSet
113 moItemSet
->SetParent(nullptr);
116 SdrObject
& rObj
= GetSdrObject();
117 rObj
.SetBoundRectDirty();
118 rObj
.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
121 mpStyleSheet
= nullptr;
124 // create a new itemset
125 SfxItemSet
AttributeProperties::CreateObjectSpecificItemSet(SfxItemPool
&)
127 assert(false && "this class is effectively abstract, should only be instantiating subclasses");
131 AttributeProperties::AttributeProperties(SdrObject
& rObj
)
132 : DefaultProperties(rObj
),
133 mpStyleSheet(nullptr)
135 // Do nothing else, esp. do *not* try to get and set
136 // a default SfxStyle sheet. Nothing is allowed to be done
137 // that may lead to calls to virtual functions like
138 // CreateObjectSpecificItemSet - these would go *wrong*.
139 // Thus the rest is lazy-init from here.
142 AttributeProperties::AttributeProperties(const AttributeProperties
& rProps
, SdrObject
& rObj
)
143 : DefaultProperties(rProps
, rObj
),
144 mpStyleSheet(nullptr)
146 SfxStyleSheet
* pTargetStyleSheet(rProps
.GetStyleSheet());
148 if(pTargetStyleSheet
)
150 const bool bModelChange(&rObj
.getSdrModelFromSdrObject() != &rProps
.GetSdrObject().getSdrModelFromSdrObject());
155 // The error shows that it is definitely necessary to solve this problem.
156 // Interestingly I already had a note here for 'work needed'.
157 // Checked in libreoffice-6-0 what happened there. In principle, the whole
158 // ::Clone of SdrPage and SdrObject happened in the same SdrModel, only
159 // afterwards a ::SetModel was used at the cloned SdrPage which went through
160 // all layers. The StyleSheet-problem was solved in
161 // AttributeProperties::MoveToItemPool at the end. There, a StyleSheet with the
162 // same name was searched for in the target-SdrModel.
163 // Start by resetting the current TargetStyleSheet so that nothing goes wrong
164 // when we do not find a fitting TargetStyleSheet.
165 // Note: The test for SdrModelChange above was wrong (compared the already set
166 // new SdrObject), so this never triggered and pTargetStyleSheet was never set to
167 // nullptr before. This means that a StyleSheet from another SdrModel was used
168 // what of course is very dangerous. Interestingly did not crash since when that
169 // other SdrModel was destroyed the ::Notify mechanism still worked reliably
170 // and de-connected this Properties successfully from the alien-StyleSheet.
171 pTargetStyleSheet
= nullptr;
173 // Check if we have a TargetStyleSheetPool at the target-SdrModel. This *should*
174 // be the case already (SdrModel::Merge and SdDrawDocument::InsertBookmarkAsPage
175 // have already cloned the StyleSheets to the target-SdrModel when used in Draw/impress).
176 // If none is found, ImpGetDefaultStyleSheet will be used to set a 'default'
177 // StyleSheet as StyleSheet implicitly later (that's what happened in the task,
178 // thus the FillStyle changed to the 'default' Blue).
179 // Note: It *may* be necessary to do more for StyleSheets, e.g. clone/copy the
180 // StyleSheet Hierarchy from the source SdrModel and/or add the Items from there
181 // as hard attributes. If needed, have a look at the older AttributeProperties::SetModel
182 // implementation from e.g. libreoffice-6-0.
183 SfxStyleSheetBasePool
* pTargetStyleSheetPool(rObj
.getSdrModelFromSdrObject().GetStyleSheetPool());
185 if(nullptr != pTargetStyleSheetPool
)
187 // If we have a TargetStyleSheetPool, search for the used StyleSheet
188 // in the target SdrModel using the Name from the original StyleSheet
189 // in the source-SdrModel.
190 pTargetStyleSheet
= dynamic_cast< SfxStyleSheet
* >(
191 pTargetStyleSheetPool
->Find(
192 rProps
.GetStyleSheet()->GetName(),
193 rProps
.GetStyleSheet()->GetFamily()));
198 if(!pTargetStyleSheet
)
203 // The SfxItemSet has been cloned and exists,
204 // we can directly set the SfxStyleSheet at it
205 ImpAddStyleSheet(pTargetStyleSheet
, true);
209 // No SfxItemSet exists yet (there is none in
210 // the source, so none was cloned). Remember the
211 // SfxStyleSheet to set it when the SfxItemSet
212 // got constructed on-demand
213 mpStyleSheet
= pTargetStyleSheet
;
217 AttributeProperties::~AttributeProperties()
219 ImpRemoveStyleSheet();
222 std::unique_ptr
<BaseProperties
> AttributeProperties::Clone(SdrObject
&) const
224 assert(false && "this class is effectively abstract, should only be instantiating subclasses");
228 const SfxItemSet
& AttributeProperties::GetObjectItemSet() const
230 // remember if we had a SfxItemSet already
231 const bool bHadSfxItemSet(HasSfxItemSet());
233 // call parent - this will guarantee SfxItemSet existence
234 DefaultProperties::GetObjectItemSet();
238 // need to take care for SfxStyleSheet for newly
239 // created SfxItemSet
240 if(nullptr == mpStyleSheet
)
242 // Set missing defaults without removal of hard attributes.
243 // This is more complicated historically than I first thought:
244 // Originally for GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj
245 // SetStyleSheet(..., false) was used, while for GetDefaultStyleSheet
246 // SetStyleSheet(..., true) was used. Thus, for SdrGrafObj and SdrOle2Obj
247 // bDontRemoveHardAttr == false -> *do* delete hard attributes was used.
248 // This was probably not done by purpose, adding the method
249 // GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj additionally to
250 // GetDefaultStyleSheet was an enhancement to allow for SdrGrafObj/SdrOle2Obj
251 // with full AttributeSet (adding e.g. FillAttributes). To stay as compatible
252 // as possible these SdrObjects got a new default-StyleSheet.
253 // There is no reason to delete the HardAttributes and it anyways has only
254 // AFAIK effects on a single Item - the SdrTextHorzAdjustItem. To get things
255 // unified I will stay with not deleting the HardAttributes and adapt the
256 // UnitTests in CppunitTest_sd_import_tests accordingly.
257 const_cast< AttributeProperties
* >(this)->applyDefaultStyleSheetFromSdrModel();
261 // Late-Init of setting parent to SfxStyleSheet after
262 // it's creation. Can only happen from copy-constructor
263 // (where creation of SfxItemSet is avoided due to the
264 // problem with constructors and virtual functions in C++),
265 // thus DontRemoveHardAttr is not needed.
266 const_cast< AttributeProperties
* >(this)->SetStyleSheet(
267 mpStyleSheet
, true, true);
274 void AttributeProperties::ItemSetChanged(std::span
< const SfxPoolItem
* const > /*aChangedItems*/, sal_uInt16
/*nDeletedWhich*/)
277 SdrObject
& rObj
= GetSdrObject();
279 rObj
.SetBoundRectDirty();
280 rObj
.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
284 void AttributeProperties::ItemChange(const sal_uInt16 nWhich
, const SfxPoolItem
* pNewItem
)
288 std::unique_ptr
<SfxPoolItem
> pResultItem
;
289 SdrModel
& rModel(GetSdrObject().getSdrModelFromSdrObject());
293 case XATTR_FILLBITMAP
:
295 // TTTT checkForUniqueItem should use SdrModel&
296 pResultItem
= static_cast<const XFillBitmapItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
301 pResultItem
= static_cast<const XLineDashItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
304 case XATTR_LINESTART
:
306 pResultItem
= static_cast<const XLineStartItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
311 pResultItem
= static_cast<const XLineEndItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
314 case XATTR_FILLGRADIENT
:
316 pResultItem
= static_cast<const XFillGradientItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
319 case XATTR_FILLFLOATTRANSPARENCE
:
321 // #85953# allow all kinds of XFillFloatTransparenceItem to be set
322 pResultItem
= static_cast<const XFillFloatTransparenceItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
325 case XATTR_FILLHATCH
:
327 pResultItem
= static_cast<const XFillHatchItem
*>(pNewItem
)->checkForUniqueItem( &rModel
);
332 // guarantee SfxItemSet existence
338 moItemSet
->Put(std::move(pResultItem
));
342 moItemSet
->Put(*pNewItem
);
347 // clear item if ItemSet exists
350 moItemSet
->ClearItem(nWhich
);
355 void AttributeProperties::SetStyleSheet(SfxStyleSheet
* pNewStyleSheet
, bool bDontRemoveHardAttr
,
358 // guarantee SfxItemSet existence
361 ImpRemoveStyleSheet();
362 ImpAddStyleSheet(pNewStyleSheet
, bDontRemoveHardAttr
);
364 SdrObject
& rObj
= GetSdrObject();
365 rObj
.SetBoundRectDirty();
366 rObj
.SetBoundAndSnapRectsDirty(true);
369 SfxStyleSheet
* AttributeProperties::GetStyleSheet() const
374 void AttributeProperties::ForceStyleToHardAttributes()
376 if(!GetStyleSheet() || mpStyleSheet
== nullptr)
379 // guarantee SfxItemSet existence
382 // prepare copied, new itemset, but WITHOUT parent
383 SfxItemSet
aDestItemSet(*moItemSet
);
384 aDestItemSet
.SetParent(nullptr);
386 // prepare forgetting the current stylesheet like in RemoveStyleSheet()
387 EndListening(*mpStyleSheet
);
388 EndListening(*mpStyleSheet
->GetPool());
390 // prepare the iter; use the mpObjectItemSet which may have less
391 // WhichIDs than the style.
392 SfxWhichIter
aIter(aDestItemSet
);
393 sal_uInt16
nWhich(aIter
.FirstWhich());
394 const SfxPoolItem
*pItem
= nullptr;
396 // now set all hard attributes of the current at the new itemset
399 // #i61284# use mpItemSet with parents, makes things easier and reduces to
401 if(SfxItemState::SET
== moItemSet
->GetItemState(nWhich
, true, &pItem
))
403 aDestItemSet
.Put(*pItem
);
406 nWhich
= aIter
.NextWhich();
410 moItemSet
.emplace(std::move(aDestItemSet
));
412 // set necessary changes like in RemoveStyleSheet()
413 GetSdrObject().SetBoundRectDirty();
414 GetSdrObject().SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
416 mpStyleSheet
= nullptr;
419 void AttributeProperties::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
421 bool bHintUsed(false);
423 const SfxStyleSheetHint
* pStyleHint
= dynamic_cast<const SfxStyleSheetHint
*>(&rHint
);
425 if(pStyleHint
&& pStyleHint
->GetStyleSheet() == GetStyleSheet())
427 SdrObject
& rObj
= GetSdrObject();
428 //SdrPage* pPage = rObj.GetPage();
430 switch(pStyleHint
->GetId())
432 case SfxHintId::StyleSheetCreated
:
434 // cannot happen, nothing to do
437 case SfxHintId::StyleSheetModified
:
438 case SfxHintId::StyleSheetChanged
:
443 case SfxHintId::StyleSheetErased
:
444 case SfxHintId::StyleSheetInDestruction
:
446 // Style needs to be exchanged
447 SfxStyleSheet
* pNewStSh
= nullptr;
448 SdrModel
& rModel(rObj
.getSdrModelFromSdrObject());
450 // Do nothing if object is in destruction, else a StyleSheet may be found from
451 // a StyleSheetPool which is just being deleted itself. and thus it would be fatal
452 // to register as listener to that new StyleSheet.
453 if(!rObj
.IsInDestruction())
455 if(SfxStyleSheet
* pStyleSheet
= GetStyleSheet())
457 pNewStSh
= static_cast<SfxStyleSheet
*>(rModel
.GetStyleSheetPool()->Find(
458 pStyleSheet
->GetParent(), pStyleSheet
->GetFamily()));
463 pNewStSh
= rModel
.GetDefaultStyleSheet();
467 // remove used style, it's erased or in destruction
468 ImpRemoveStyleSheet();
472 ImpAddStyleSheet(pNewStSh
, true);
480 // Get old BoundRect. Do this after the style change is handled
481 // in the ItemSet parts because GetBoundRect() may calculate a new
482 tools::Rectangle aBoundRect
= rObj
.GetLastBoundRect();
484 rObj
.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
486 // tell the object about the change
488 rObj
.BroadcastObjectChange();
490 //if(pPage && pPage->IsInserted())
492 // rObj.BroadcastObjectChange();
495 rObj
.SendUserCall(SdrUserCallType::ChangeAttr
, aBoundRect
);
502 // forward to SdrObject ATM. Not sure if this will be necessary
504 GetSdrObject().Notify(rBC
, rHint
);
508 bool AttributeProperties::isUsedByModel() const
510 const SdrObject
& rObj(GetSdrObject());
511 if (rObj
.IsInserted())
513 const SdrPage
* const pPage(rObj
.getSdrPageFromSdrObject());
514 if (pPage
&& pPage
->IsInserted())
520 void AttributeProperties::applyDefaultStyleSheetFromSdrModel()
522 SfxStyleSheet
* pDefaultStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheet());
524 // tdf#118139 Only do this when StyleSheet really differs. It may e.g.
525 // be the case that nullptr == pDefaultStyleSheet and there is none set yet,
526 // so indeed no need to set it (needed for some strange old MSWord2003
527 // documents with CustomShape-'Group' and added Text-Frames, see task description)
528 if(pDefaultStyleSheet
!= GetStyleSheet())
530 // do not delete hard attributes when setting dsefault Style
531 SetStyleSheet(pDefaultStyleSheet
, true, true);
535 } // end of namespace
537 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */