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