LibreOffice Module svx (master)  1
attributeproperties.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
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 
40 namespace sdr::properties
41 {
42  void AttributeProperties::ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr)
43  {
45  {
46  // Delete hard attributes where items are set in the style sheet
47  if(!bDontRemoveHardAttr)
48  {
49  const SfxItemSet& rStyle = mpStyleSheet->GetItemSet();
50  SfxWhichIter aIter(rStyle);
51  sal_uInt16 nWhich = aIter.FirstWhich();
52 
53  while(nWhich)
54  {
55  if(SfxItemState::SET == rStyle.GetItemState(nWhich))
56  {
57  mpItemSet->ClearItem(nWhich);
58  }
59 
60  nWhich = aIter.NextWhich();
61  }
62  }
63 
64  // set new stylesheet as parent
65  mpItemSet->SetParent(&mpStyleSheet->GetItemSet());
66  }
67  else
68  {
69  OSL_ENSURE(false, "ImpSetParentAtSfxItemSet called without SfxItemSet/SfxStyleSheet (!)");
70  }
71  }
72 
73  void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
74  {
75  // test if old StyleSheet is cleared, else it would be lost
76  // after this method -> memory leak (!)
77  DBG_ASSERT(!mpStyleSheet, "Old style sheet not deleted before setting new one (!)");
78 
79  if(pNewStyleSheet)
80  {
81  // local remember
82  mpStyleSheet = pNewStyleSheet;
83 
84  if(HasSfxItemSet())
85  {
86  // register as listener
87  StartListening(*pNewStyleSheet->GetPool());
88  StartListening(*pNewStyleSheet);
89 
90  // only apply the following when we have an SfxItemSet already, else
91  if(GetStyleSheet())
92  {
93  ImpSetParentAtSfxItemSet(bDontRemoveHardAttr);
94  }
95  }
96  }
97  }
98 
100  {
101  // Check type since it is destroyed when the type is deleted
102  if(GetStyleSheet() && dynamic_cast<const SfxStyleSheet *>(mpStyleSheet) != nullptr)
103  {
105  if (auto const pool = mpStyleSheet->GetPool()) { // TTTT
106  EndListening(*pool);
107  }
108 
109  // reset parent of ItemSet
110  if(HasSfxItemSet())
111  {
112  mpItemSet->SetParent(nullptr);
113  }
114 
115  SdrObject& rObj = GetSdrObject();
116  rObj.SetBoundRectDirty();
117  rObj.SetRectsDirty(true);
118  }
119 
120  mpStyleSheet = nullptr;
121  }
122 
123  // create a new itemset
125  {
126  return std::make_unique<SfxItemSet>(rPool,
127 
128  // ranges from SdrAttrObj
131  SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION>{});
132  }
133 
135  : DefaultProperties(rObj),
136  mpStyleSheet(nullptr)
137  {
138  // Do nothing else, esp. do *not* try to get and set
139  // a default SfxStyle sheet. Nothing is allowed to be done
140  // that may lead to calls to virtual functions like
141  // CreateObjectSpecificItemSet - these would go *wrong*.
142  // Thus the rest is lazy-init from here.
143  }
144 
146  : DefaultProperties(rProps, rObj),
147  mpStyleSheet(nullptr)
148  {
149  SfxStyleSheet* pTargetStyleSheet(rProps.GetStyleSheet());
150 
151  if(pTargetStyleSheet)
152  {
153  const bool bModelChange(&rObj.getSdrModelFromSdrObject() != &rProps.GetSdrObject().getSdrModelFromSdrObject());
154 
155  if(bModelChange)
156  {
157  // tdf#117506
158  // The error shows that it is definitely necessary to solve this problem.
159  // Interestingly I already had a note here for 'work needed'.
160  // Checked in libreoffice-6-0 what happened there. In principle, the whole
161  // ::Clone of SdrPage and SdrObject happened in the same SdrModel, only
162  // afterwards a ::SetModel was used at the cloned SdrPage which went through
163  // all layers. The StyleSheet-problem was solved in
164  // AttributeProperties::MoveToItemPool at the end. There, a StyleSheet with the
165  // same name was searched for in the target-SdrModel.
166  // Start by resetting the current TargetStyleSheet so that nothing goes wrong
167  // when we do not find a fitting TargetStyleSheet.
168  // Note: The test for SdrModelChange above was wrong (compared the already set
169  // new SdrObject), so this never triggered and pTargetStyleSheet was never set to
170  // nullptr before. This means that a StyleSheet from another SdrModel was used
171  // what of course is very dangerous. Interestingly did not crash since when that
172  // other SdrModel was destroyed the ::Notify mechanism still worked reliably
173  // and de-connected this Properties successfully from the alien-StyleSheet.
174  pTargetStyleSheet = nullptr;
175 
176  // Check if we have a TargetStyleSheetPool at the target-SdrModel. This *should*
177  // be the case already (SdrModel::Merge and SdDrawDocument::InsertBookmarkAsPage
178  // have already cloned the StyleSheets to the target-SdrModel when used in Draw/impress).
179  // If none is found, ImpGetDefaultStyleSheet will be used to set a 'default'
180  // StyleSheet as StyleSheet implicitly later (that's what happened in the task,
181  // thus the FillStyle changed to the 'default' Blue).
182  // Note: It *may* be necessary to do more for StyleSheets, e.g. clone/copy the
183  // StyleSheet Hierarchy from the source SdrModel and/or add the Items from there
184  // as hard attributes. If needed, have a look at the older AttributeProperties::SetModel
185  // implementation from e.g. libreoffice-6-0.
186  SfxStyleSheetBasePool* pTargetStyleSheetPool(rObj.getSdrModelFromSdrObject().GetStyleSheetPool());
187 
188  if(nullptr != pTargetStyleSheetPool)
189  {
190  // If we have a TargetStyleSheetPool, search for the used StyleSheet
191  // in the target SdrModel using the Name from the original StyleSheet
192  // in the source-SdrModel.
193  pTargetStyleSheet = dynamic_cast< SfxStyleSheet* >(
194  pTargetStyleSheetPool->Find(
195  rProps.GetStyleSheet()->GetName(),
196  SfxStyleFamily::All));
197  }
198  }
199  }
200 
201  if(pTargetStyleSheet)
202  {
203  if(HasSfxItemSet())
204  {
205  // The SfxItemSet has been cloned and exists,
206  // we can directly set the SfxStyleSheet at it
207  ImpAddStyleSheet(pTargetStyleSheet, true);
208  }
209  else
210  {
211  // No SfxItemSet exists yet (there is none in
212  // the source, so none was cloned). Remember the
213  // SfxStyleSheet to set it when the SfxItemSet
214  // got constructed on-demand
215  mpStyleSheet = pTargetStyleSheet;
216  }
217  }
218  }
219 
221  {
223  }
224 
225  std::unique_ptr<BaseProperties> AttributeProperties::Clone(SdrObject& rObj) const
226  {
227  return std::unique_ptr<BaseProperties>(new AttributeProperties(*this, rObj));
228  }
229 
231  {
232  // remember if we had a SfxItemSet already
233  const bool bHadSfxItemSet(HasSfxItemSet());
234 
235  // call parent - this will guarantee SfxItemSet existence
237 
238  if(!bHadSfxItemSet)
239  {
240  // need to take care for SfxStyleSheet for newly
241  // created SfxItemSet
242  if(nullptr == mpStyleSheet)
243  {
244  // Set missing defaults without removal of hard attributes.
245  // This is more complicated historically than I first thought:
246  // Originally for GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj
247  // SetStyleSheet(..., false) was used, while for GetDefaultStyleSheet
248  // SetStyleSheet(..., true) was used. Thus, for SdrGrafObj and SdrOle2Obj
249  // bDontRemoveHardAttr == false -> *do* delete hard attributes was used.
250  // This was probably not done by purpose, adding the method
251  // GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj additionally to
252  // GetDefaultStyleSheet was an enhancement to allow for SdrGrafObj/SdrOle2Obj
253  // with full AttributeSet (adding e.g. FillAttributes). To stay as compatible
254  // as possible these SdrObjects got a new default-StyleSheet.
255  // There is no reason to delete the HardAttributes and it anyways has only
256  // AFAIK effects on a single Item - the SdrTextHorzAdjustItem. To get things
257  // unified I will stay with not deleting the HardAttributes and adapt the
258  // UnitTests in CppunitTest_sd_import_tests accordingly.
260  }
261  else
262  {
263  // Late-Init of setting parent to SfxStyleSheet after
264  // it's creation. Can only happen from copy-constructor
265  // (where creation of SfxItemSet is avoided due to the
266  // problem with constructors and virtual functions in C++),
267  // thus DontRemoveHardAttr is not needed.
268  const_cast< AttributeProperties* >(this)->SetStyleSheet(
269  mpStyleSheet,
270  true);
271  }
272  }
273 
274  return *mpItemSet;
275  }
276 
278  {
279  // own modifications
280  SdrObject& rObj = GetSdrObject();
281 
282  rObj.SetBoundRectDirty();
283  rObj.SetRectsDirty(true);
284  rObj.SetChanged();
285  }
286 
287  void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
288  {
289  if(pNewItem)
290  {
291  std::unique_ptr<SfxPoolItem> pResultItem;
292  SdrModel& rModel(GetSdrObject().getSdrModelFromSdrObject());
293 
294  switch( nWhich )
295  {
296  case XATTR_FILLBITMAP:
297  {
298  // TTTT checkForUniqueItem should use SdrModel&
299  pResultItem = static_cast<const XFillBitmapItem*>(pNewItem)->checkForUniqueItem( &rModel );
300  break;
301  }
302  case XATTR_LINEDASH:
303  {
304  pResultItem = static_cast<const XLineDashItem*>(pNewItem)->checkForUniqueItem( &rModel );
305  break;
306  }
307  case XATTR_LINESTART:
308  {
309  pResultItem = static_cast<const XLineStartItem*>(pNewItem)->checkForUniqueItem( &rModel );
310  break;
311  }
312  case XATTR_LINEEND:
313  {
314  pResultItem = static_cast<const XLineEndItem*>(pNewItem)->checkForUniqueItem( &rModel );
315  break;
316  }
317  case XATTR_FILLGRADIENT:
318  {
319  pResultItem = static_cast<const XFillGradientItem*>(pNewItem)->checkForUniqueItem( &rModel );
320  break;
321  }
323  {
324  // #85953# allow all kinds of XFillFloatTransparenceItem to be set
325  pResultItem = static_cast<const XFillFloatTransparenceItem*>(pNewItem)->checkForUniqueItem( &rModel );
326  break;
327  }
328  case XATTR_FILLHATCH:
329  {
330  pResultItem = static_cast<const XFillHatchItem*>(pNewItem)->checkForUniqueItem( &rModel );
331  break;
332  }
333  }
334 
335  // guarantee SfxItemSet existence
337 
338  if(pResultItem)
339  {
340  // force ItemSet
341  mpItemSet->Put(*pResultItem);
342 
343  // delete item if it was a generated one
344  pResultItem.reset();
345  }
346  else
347  {
348  mpItemSet->Put(*pNewItem);
349  }
350  }
351  else
352  {
353  // clear item if ItemSet exists
354  if(HasSfxItemSet())
355  {
356  mpItemSet->ClearItem(nWhich);
357  }
358  }
359  }
360 
361  void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
362  {
363  // guarantee SfxItemSet existence
365 
367  ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
368 
369  SdrObject& rObj = GetSdrObject();
370  rObj.SetBoundRectDirty();
371  rObj.SetRectsDirty(true);
372  }
373 
375  {
376  return mpStyleSheet;
377  }
378 
380  {
381  if(!GetStyleSheet() || dynamic_cast<const SfxStyleSheet *>(mpStyleSheet) == nullptr)
382  return;
383 
384  // guarantee SfxItemSet existence
386 
387  // prepare copied, new itemset, but WITHOUT parent
388  SfxItemSet* pDestItemSet = new SfxItemSet(*mpItemSet);
389  pDestItemSet->SetParent(nullptr);
390 
391  // prepare forgetting the current stylesheet like in RemoveStyleSheet()
394 
395  // prepare the iter; use the mpObjectItemSet which may have less
396  // WhichIDs than the style.
397  SfxWhichIter aIter(*pDestItemSet);
398  sal_uInt16 nWhich(aIter.FirstWhich());
399  const SfxPoolItem *pItem = nullptr;
400 
401  // now set all hard attributes of the current at the new itemset
402  while(nWhich)
403  {
404  // #i61284# use mpItemSet with parents, makes things easier and reduces to
405  // one loop
406  if(SfxItemState::SET == mpItemSet->GetItemState(nWhich, true, &pItem))
407  {
408  pDestItemSet->Put(*pItem);
409  }
410 
411  nWhich = aIter.NextWhich();
412  }
413 
414  // replace itemsets
415  mpItemSet.reset(pDestItemSet);
416 
417  // set necessary changes like in RemoveStyleSheet()
419  GetSdrObject().SetRectsDirty(true);
420 
421  mpStyleSheet = nullptr;
422  }
423 
425  {
426  bool bHintUsed(false);
427 
428  const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint);
429 
430  if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet())
431  {
432  SdrObject& rObj = GetSdrObject();
433  //SdrPage* pPage = rObj.GetPage();
434 
435  switch(pStyleHint->GetId())
436  {
437  case SfxHintId::StyleSheetCreated :
438  {
439  // cannot happen, nothing to do
440  break;
441  }
442  case SfxHintId::StyleSheetModified :
443  case SfxHintId::StyleSheetChanged :
444  {
445  // notify change
446  break;
447  }
448  case SfxHintId::StyleSheetErased :
449  case SfxHintId::StyleSheetInDestruction :
450  {
451  // Style needs to be exchanged
452  SfxStyleSheet* pNewStSh = nullptr;
453  SdrModel& rModel(rObj.getSdrModelFromSdrObject());
454 
455  // Do nothing if object is in destruction, else a StyleSheet may be found from
456  // a StyleSheetPool which is just being deleted itself. and thus it would be fatal
457  // to register as listener to that new StyleSheet.
458  if(!rObj.IsInDestruction())
459  {
460  if(dynamic_cast<const SfxStyleSheet *>(GetStyleSheet()) != nullptr)
461  {
462  pNewStSh = static_cast<SfxStyleSheet*>(rModel.GetStyleSheetPool()->Find(
463  GetStyleSheet()->GetParent(), GetStyleSheet()->GetFamily()));
464  }
465 
466  if(!pNewStSh)
467  {
468  pNewStSh = rModel.GetDefaultStyleSheet();
469  }
470  }
471 
472  // remove used style, it's erased or in destruction
474 
475  if(pNewStSh)
476  {
477  ImpAddStyleSheet(pNewStSh, true);
478  }
479 
480  break;
481  }
482  default: break;
483  }
484 
485  // Get old BoundRect. Do this after the style change is handled
486  // in the ItemSet parts because GetBoundRect() may calculate a new
487  tools::Rectangle aBoundRect = rObj.GetLastBoundRect();
488 
489  rObj.SetRectsDirty(true);
490 
491  // tell the object about the change
492  rObj.SetChanged();
493  rObj.BroadcastObjectChange();
494 
495  //if(pPage && pPage->IsInserted())
496  //{
497  // rObj.BroadcastObjectChange();
498  //}
499 
500  rObj.SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect);
501 
502  bHintUsed = true;
503  }
504 
505  if(!bHintUsed)
506  {
507  // forward to SdrObject ATM. Not sure if this will be necessary
508  // in the future.
509  GetSdrObject().Notify(rBC, rHint);
510  }
511  }
512 
514  {
515  const SdrObject& rObj(GetSdrObject());
516  if (rObj.IsInserted())
517  {
518  const SdrPage* const pPage(rObj.getSdrPageFromSdrObject());
519  if (pPage && pPage->IsInserted())
520  return true;
521  }
522  return false;
523  }
524 } // end of namespace
525 
526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual SfxStyleSheet * GetStyleSheet() const override
constexpr TypedWhichId< XLineEndItem > XATTR_LINEEND(XATTR_LINE_FIRST+5)
constexpr TypedWhichId< XLineStartItem > XATTR_LINESTART(XATTR_LINE_FIRST+4)
bool IsInDestruction() const
Definition: svdobj.cxx:2948
virtual bool isUsedByModel() const override
virtual const SfxItemSet & GetObjectItemSet() const override
bool IsInserted() const
Definition: svdobj.hxx:793
virtual void SetBoundRectDirty()
Definition: svdobj.cxx:310
sal_uInt16 FirstWhich()
virtual const SfxItemSet & GetObjectItemSet() const override
virtual SfxItemSet & GetItemSet()
constexpr TypedWhichId< XLineDashItem > XATTR_LINEDASH(XATTR_LINE_FIRST+1)
constexpr TypedWhichId< XFillHatchItem > XATTR_FILLHATCH(XATTR_FILL_FIRST+3)
constexpr sal_uInt16 SDRATTR_MISC_LAST(SDRATTR_TEXT_CHAINNEXTNAME)
std::unique_ptr< SfxItemSet > mpItemSet
SfxHintId GetId() const
constexpr sal_uInt16 SDRATTR_START(XATTR_START)
sal_uInt16 NextWhich()
SfxStyleSheetBase * GetStyleSheet() const
const OUString & GetName() const
void ImpAddStyleSheet(SfxStyleSheet *pNewStyleSheet, bool bDontRemoveHardAttr)
SdrPage * getSdrPageFromSdrObject() const
Definition: svdobj.cxx:263
virtual void SetStyleSheet(SfxStyleSheet *pNewStyleSheet, bool bDontRemoveHardAttr) override
SfxStyleSheetBasePool * GetPool()
constexpr TypedWhichId< SvxWritingModeItem > SDRATTR_TEXTDIRECTION(SDRATTR_NOTPERSIST_FIRST+34)
constexpr TypedWhichId< XFillGradientItem > XATTR_FILLGRADIENT(XATTR_FILL_FIRST+2)
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
#define DBG_ASSERT(sCon, aError)
constexpr TypedWhichId< XFillBitmapItem > XATTR_FILLBITMAP(XATTR_FILL_FIRST+4)
void BroadcastObjectChange() const
Definition: svdobj.cxx:905
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:273
virtual std::unique_ptr< BaseProperties > Clone(SdrObject &rObj) const override
void StartListening(SfxBroadcaster &rBroadcaster, DuplicateHandling eDuplicateHanding=DuplicateHandling::Unexpected)
Abstract DrawObject.
Definition: svdobj.hxx:312
virtual void ItemSetChanged(const SfxItemSet &rSet) override
virtual void applyDefaultStyleSheetFromSdrModel()
Definition: properties.cxx:44
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
void SetParent(const SfxItemSet *pNew)
virtual const tools::Rectangle & GetLastBoundRect() const
Definition: svdobj.cxx:868
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint)
const SfxPoolItem * Put(const SfxPoolItem &rItem, sal_uInt16 nWhich)
constexpr sal_uInt16 SDRATTR_SHADOW_LAST(SDRATTR_SHADOWBLUR)
constexpr TypedWhichId< XFillFloatTransparenceItem > XATTR_FILLFLOATTRANSPARENCE(XATTR_FILL_FIRST+11)
virtual void ForceStyleToHardAttributes() override
virtual std::unique_ptr< SfxItemSet > CreateObjectSpecificItemSet(SfxItemPool &pPool) override
SfxStyleSheetBasePool * GetStyleSheetPool() const
Definition: svdmodel.hxx:538
void EndListening(SfxBroadcaster &rBroadcaster, bool bRemoveAllDuplicates=false)
virtual void SetRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:435
const SdrObject & GetSdrObject() const
Definition: properties.cxx:59
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:365
void ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr)
virtual void SetChanged()
Definition: svdobj.cxx:929
constexpr sal_uInt16 SDRATTR_MISC_FIRST(SDRATTR_CAPTION_LAST+1)
virtual void ItemChange(const sal_uInt16 nWhich, const SfxPoolItem *pNewItem=nullptr) override
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2654