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 == aIter.GetItemState())
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.SetBoundAndSnapRectsDirty(/*bNotMyself*/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, true, true);
272  }
273  }
274 
275  return *mxItemSet;
276  }
277 
278  void AttributeProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/)
279  {
280  // own modifications
281  SdrObject& rObj = GetSdrObject();
282 
283  rObj.SetBoundRectDirty();
284  rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/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  mxItemSet->Put(std::move(pResultItem));
343  }
344  else
345  {
346  mxItemSet->Put(*pNewItem);
347  }
348  }
349  else
350  {
351  // clear item if ItemSet exists
352  if(HasSfxItemSet())
353  {
354  mxItemSet->ClearItem(nWhich);
355  }
356  }
357  }
358 
359  void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
360  bool /*bBroadcast*/)
361  {
362  // guarantee SfxItemSet existence
364 
366  ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
367 
368  SdrObject& rObj = GetSdrObject();
369  rObj.SetBoundRectDirty();
370  rObj.SetBoundAndSnapRectsDirty(true);
371  }
372 
374  {
375  return mpStyleSheet;
376  }
377 
379  {
380  if(!GetStyleSheet() || mpStyleSheet == nullptr)
381  return;
382 
383  // guarantee SfxItemSet existence
385 
386  // prepare copied, new itemset, but WITHOUT parent
387  SfxItemSet aDestItemSet(*mxItemSet);
388  aDestItemSet.SetParent(nullptr);
389 
390  // prepare forgetting the current stylesheet like in RemoveStyleSheet()
393 
394  // prepare the iter; use the mpObjectItemSet which may have less
395  // WhichIDs than the style.
396  SfxWhichIter aIter(aDestItemSet);
397  sal_uInt16 nWhich(aIter.FirstWhich());
398  const SfxPoolItem *pItem = nullptr;
399 
400  // now set all hard attributes of the current at the new itemset
401  while(nWhich)
402  {
403  // #i61284# use mpItemSet with parents, makes things easier and reduces to
404  // one loop
405  if(SfxItemState::SET == mxItemSet->GetItemState(nWhich, true, &pItem))
406  {
407  aDestItemSet.Put(*pItem);
408  }
409 
410  nWhich = aIter.NextWhich();
411  }
412 
413  // replace itemsets
414  mxItemSet.emplace(std::move(aDestItemSet));
415 
416  // set necessary changes like in RemoveStyleSheet()
418  GetSdrObject().SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
419 
420  mpStyleSheet = nullptr;
421  }
422 
424  {
425  bool bHintUsed(false);
426 
427  const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint);
428 
429  if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet())
430  {
431  SdrObject& rObj = GetSdrObject();
432  //SdrPage* pPage = rObj.GetPage();
433 
434  switch(pStyleHint->GetId())
435  {
436  case SfxHintId::StyleSheetCreated :
437  {
438  // cannot happen, nothing to do
439  break;
440  }
441  case SfxHintId::StyleSheetModified :
442  case SfxHintId::StyleSheetChanged :
443  {
444  // notify change
445  break;
446  }
447  case SfxHintId::StyleSheetErased :
448  case SfxHintId::StyleSheetInDestruction :
449  {
450  // Style needs to be exchanged
451  SfxStyleSheet* pNewStSh = nullptr;
452  SdrModel& rModel(rObj.getSdrModelFromSdrObject());
453 
454  // Do nothing if object is in destruction, else a StyleSheet may be found from
455  // a StyleSheetPool which is just being deleted itself. and thus it would be fatal
456  // to register as listener to that new StyleSheet.
457  if(!rObj.IsInDestruction())
458  {
459  if(SfxStyleSheet* pStyleSheet = GetStyleSheet())
460  {
461  pNewStSh = static_cast<SfxStyleSheet*>(rModel.GetStyleSheetPool()->Find(
462  pStyleSheet->GetParent(), pStyleSheet->GetFamily()));
463  }
464 
465  if(!pNewStSh)
466  {
467  pNewStSh = rModel.GetDefaultStyleSheet();
468  }
469  }
470 
471  // remove used style, it's erased or in destruction
473 
474  if(pNewStSh)
475  {
476  ImpAddStyleSheet(pNewStSh, true);
477  }
478 
479  break;
480  }
481  default: break;
482  }
483 
484  // Get old BoundRect. Do this after the style change is handled
485  // in the ItemSet parts because GetBoundRect() may calculate a new
486  tools::Rectangle aBoundRect = rObj.GetLastBoundRect();
487 
488  rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
489 
490  // tell the object about the change
491  rObj.SetChanged();
492  rObj.BroadcastObjectChange();
493 
494  //if(pPage && pPage->IsInserted())
495  //{
496  // rObj.BroadcastObjectChange();
497  //}
498 
499  rObj.SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect);
500 
501  bHintUsed = true;
502  }
503 
504  if(!bHintUsed)
505  {
506  // forward to SdrObject ATM. Not sure if this will be necessary
507  // in the future.
508  GetSdrObject().Notify(rBC, rHint);
509  }
510  }
511 
513  {
514  const SdrObject& rObj(GetSdrObject());
515  if (rObj.IsInserted())
516  {
517  const SdrPage* const pPage(rObj.getSdrPageFromSdrObject());
518  if (pPage && pPage->IsInserted())
519  return true;
520  }
521  return false;
522  }
523 
525  {
526  SfxStyleSheet* pDefaultStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheet());
527 
528  // tdf#118139 Only do this when StyleSheet really differs. It may e.g.
529  // be the case that nullptr == pDefaultStyleSheet and there is none set yet,
530  // so indeed no need to set it (needed for some strange old MSWord2003
531  // documents with CustomShape-'Group' and added Text-Frames, see task description)
532  if(pDefaultStyleSheet != GetStyleSheet())
533  {
534  // do not delete hard attributes when setting dsefault Style
535  SetStyleSheet(pDefaultStyleSheet, true, true);
536  }
537  }
538 
539 } // end of namespace
540 
541 /* 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:3063
virtual bool isUsedByModel() const override
virtual const SfxItemSet & GetObjectItemSet() const override
bool IsInserted() const
Definition: svdobj.hxx:745
virtual void SetBoundRectDirty()
Definition: svdobj.cxx:321
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:274
virtual void SetBoundAndSnapRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:515
SfxStyleSheetBasePool * GetPool()
constexpr TypedWhichId< SvxWritingModeItem > SDRATTR_TEXTDIRECTION(SDRATTR_NOTPERSIST_FIRST+34)
constexpr TypedWhichId< XFillGradientItem > XATTR_FILLGRADIENT(XATTR_FILL_FIRST+2)
#define DBG_ASSERT(sCon, aError)
constexpr TypedWhichId< XFillBitmapItem > XATTR_FILLBITMAP(XATTR_FILL_FIRST+4)
void BroadcastObjectChange() const
Definition: svdobj.cxx:1006
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:284
virtual std::unique_ptr< BaseProperties > Clone(SdrObject &rObj) const override
void StartListening(SfxBroadcaster &rBroadcaster, DuplicateHandling eDuplicateHanding=DuplicateHandling::Unexpected)
Abstract DrawObject.
Definition: svdobj.hxx:260
SfxItemState GetItemState(bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
void SetParent(const SfxItemSet *pNew)
virtual const tools::Rectangle & GetLastBoundRect() const
Definition: svdobj.cxx:968
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:541
void EndListening(SfxBroadcaster &rBroadcaster, bool bRemoveAllDuplicates=false)
std::optional< SfxItemSet > mxItemSet
const SdrObject & GetSdrObject() const
Definition: properties.cxx:43
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:373
void ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr)
virtual void ItemSetChanged(o3tl::span< const SfxPoolItem *const > aChangedItems, sal_uInt16 nDeletedWhich) override
virtual void SetChanged()
Definition: svdobj.cxx:1030
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:2767
virtual void SetStyleSheet(SfxStyleSheet *pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast) override