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