diff options
Diffstat (limited to 'svx/source/sdr/properties/attributeproperties.cxx')
-rw-r--r-- | svx/source/sdr/properties/attributeproperties.cxx | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/svx/source/sdr/properties/attributeproperties.cxx b/svx/source/sdr/properties/attributeproperties.cxx new file mode 100644 index 000000000..7f141b8f6 --- /dev/null +++ b/svx/source/sdr/properties/attributeproperties.cxx @@ -0,0 +1,541 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <sdr/properties/attributeproperties.hxx> +#include <tools/debug.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svl/whiter.hxx> +#include <svl/poolitem.hxx> +#include <svx/svdobj.hxx> +#include <svx/svddef.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <osl/diagnose.h> + +namespace sdr::properties +{ + void AttributeProperties::ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr) + { + if(HasSfxItemSet() && mpStyleSheet) + { + // Delete hard attributes where items are set in the style sheet + if(!bDontRemoveHardAttr) + { + const SfxItemSet& rStyle = mpStyleSheet->GetItemSet(); + SfxWhichIter aIter(rStyle); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + if(SfxItemState::SET == aIter.GetItemState()) + { + mxItemSet->ClearItem(nWhich); + } + + nWhich = aIter.NextWhich(); + } + } + + // set new stylesheet as parent + mxItemSet->SetParent(&mpStyleSheet->GetItemSet()); + } + else + { + OSL_ENSURE(false, "ImpSetParentAtSfxItemSet called without SfxItemSet/SfxStyleSheet (!)"); + } + } + + void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr) + { + // test if old StyleSheet is cleared, else it would be lost + // after this method -> memory leak (!) + DBG_ASSERT(!mpStyleSheet, "Old style sheet not deleted before setting new one (!)"); + + if(!pNewStyleSheet) + return; + + // local remember + mpStyleSheet = pNewStyleSheet; + + if(HasSfxItemSet()) + { + // register as listener + StartListening(*pNewStyleSheet->GetPool()); + StartListening(*pNewStyleSheet); + + // only apply the following when we have an SfxItemSet already, else + if(GetStyleSheet()) + { + ImpSetParentAtSfxItemSet(bDontRemoveHardAttr); + } + } + } + + void AttributeProperties::ImpRemoveStyleSheet() + { + // Check type since it is destroyed when the type is deleted + if(GetStyleSheet() && mpStyleSheet) + { + EndListening(*mpStyleSheet); + if (auto const pool = mpStyleSheet->GetPool()) { // TTTT + EndListening(*pool); + } + + // reset parent of ItemSet + if(HasSfxItemSet()) + { + mxItemSet->SetParent(nullptr); + } + + SdrObject& rObj = GetSdrObject(); + rObj.SetBoundRectDirty(); + rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + } + + mpStyleSheet = nullptr; + } + + // create a new itemset + SfxItemSet AttributeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool, + + // ranges from SdrAttrObj + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST>); + } + + AttributeProperties::AttributeProperties(SdrObject& rObj) + : DefaultProperties(rObj), + mpStyleSheet(nullptr) + { + // Do nothing else, esp. do *not* try to get and set + // a default SfxStyle sheet. Nothing is allowed to be done + // that may lead to calls to virtual functions like + // CreateObjectSpecificItemSet - these would go *wrong*. + // Thus the rest is lazy-init from here. + } + + AttributeProperties::AttributeProperties(const AttributeProperties& rProps, SdrObject& rObj) + : DefaultProperties(rProps, rObj), + mpStyleSheet(nullptr) + { + SfxStyleSheet* pTargetStyleSheet(rProps.GetStyleSheet()); + + if(pTargetStyleSheet) + { + const bool bModelChange(&rObj.getSdrModelFromSdrObject() != &rProps.GetSdrObject().getSdrModelFromSdrObject()); + + if(bModelChange) + { + // tdf#117506 + // The error shows that it is definitely necessary to solve this problem. + // Interestingly I already had a note here for 'work needed'. + // Checked in libreoffice-6-0 what happened there. In principle, the whole + // ::Clone of SdrPage and SdrObject happened in the same SdrModel, only + // afterwards a ::SetModel was used at the cloned SdrPage which went through + // all layers. The StyleSheet-problem was solved in + // AttributeProperties::MoveToItemPool at the end. There, a StyleSheet with the + // same name was searched for in the target-SdrModel. + // Start by resetting the current TargetStyleSheet so that nothing goes wrong + // when we do not find a fitting TargetStyleSheet. + // Note: The test for SdrModelChange above was wrong (compared the already set + // new SdrObject), so this never triggered and pTargetStyleSheet was never set to + // nullptr before. This means that a StyleSheet from another SdrModel was used + // what of course is very dangerous. Interestingly did not crash since when that + // other SdrModel was destroyed the ::Notify mechanism still worked reliably + // and de-connected this Properties successfully from the alien-StyleSheet. + pTargetStyleSheet = nullptr; + + // Check if we have a TargetStyleSheetPool at the target-SdrModel. This *should* + // be the case already (SdrModel::Merge and SdDrawDocument::InsertBookmarkAsPage + // have already cloned the StyleSheets to the target-SdrModel when used in Draw/impress). + // If none is found, ImpGetDefaultStyleSheet will be used to set a 'default' + // StyleSheet as StyleSheet implicitly later (that's what happened in the task, + // thus the FillStyle changed to the 'default' Blue). + // Note: It *may* be necessary to do more for StyleSheets, e.g. clone/copy the + // StyleSheet Hierarchy from the source SdrModel and/or add the Items from there + // as hard attributes. If needed, have a look at the older AttributeProperties::SetModel + // implementation from e.g. libreoffice-6-0. + SfxStyleSheetBasePool* pTargetStyleSheetPool(rObj.getSdrModelFromSdrObject().GetStyleSheetPool()); + + if(nullptr != pTargetStyleSheetPool) + { + // If we have a TargetStyleSheetPool, search for the used StyleSheet + // in the target SdrModel using the Name from the original StyleSheet + // in the source-SdrModel. + pTargetStyleSheet = dynamic_cast< SfxStyleSheet* >( + pTargetStyleSheetPool->Find( + rProps.GetStyleSheet()->GetName(), + SfxStyleFamily::All)); + } + } + } + + if(!pTargetStyleSheet) + return; + + if(HasSfxItemSet()) + { + // The SfxItemSet has been cloned and exists, + // we can directly set the SfxStyleSheet at it + ImpAddStyleSheet(pTargetStyleSheet, true); + } + else + { + // No SfxItemSet exists yet (there is none in + // the source, so none was cloned). Remember the + // SfxStyleSheet to set it when the SfxItemSet + // got constructed on-demand + mpStyleSheet = pTargetStyleSheet; + } + } + + AttributeProperties::~AttributeProperties() + { + ImpRemoveStyleSheet(); + } + + std::unique_ptr<BaseProperties> AttributeProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new AttributeProperties(*this, rObj)); + } + + const SfxItemSet& AttributeProperties::GetObjectItemSet() const + { + // remember if we had a SfxItemSet already + const bool bHadSfxItemSet(HasSfxItemSet()); + + // call parent - this will guarantee SfxItemSet existence + DefaultProperties::GetObjectItemSet(); + + if(!bHadSfxItemSet) + { + // need to take care for SfxStyleSheet for newly + // created SfxItemSet + if(nullptr == mpStyleSheet) + { + // Set missing defaults without removal of hard attributes. + // This is more complicated historically than I first thought: + // Originally for GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj + // SetStyleSheet(..., false) was used, while for GetDefaultStyleSheet + // SetStyleSheet(..., true) was used. Thus, for SdrGrafObj and SdrOle2Obj + // bDontRemoveHardAttr == false -> *do* delete hard attributes was used. + // This was probably not done by purpose, adding the method + // GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj additionally to + // GetDefaultStyleSheet was an enhancement to allow for SdrGrafObj/SdrOle2Obj + // with full AttributeSet (adding e.g. FillAttributes). To stay as compatible + // as possible these SdrObjects got a new default-StyleSheet. + // There is no reason to delete the HardAttributes and it anyways has only + // AFAIK effects on a single Item - the SdrTextHorzAdjustItem. To get things + // unified I will stay with not deleting the HardAttributes and adapt the + // UnitTests in CppunitTest_sd_import_tests accordingly. + const_cast< AttributeProperties* >(this)->applyDefaultStyleSheetFromSdrModel(); + } + else + { + // Late-Init of setting parent to SfxStyleSheet after + // it's creation. Can only happen from copy-constructor + // (where creation of SfxItemSet is avoided due to the + // problem with constructors and virtual functions in C++), + // thus DontRemoveHardAttr is not needed. + const_cast< AttributeProperties* >(this)->SetStyleSheet( + mpStyleSheet, true, true); + } + } + + return *mxItemSet; + } + + void AttributeProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/) + { + // own modifications + SdrObject& rObj = GetSdrObject(); + + rObj.SetBoundRectDirty(); + rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + rObj.SetChanged(); + } + + void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem) + { + if(pNewItem) + { + std::unique_ptr<SfxPoolItem> pResultItem; + SdrModel& rModel(GetSdrObject().getSdrModelFromSdrObject()); + + switch( nWhich ) + { + case XATTR_FILLBITMAP: + { + // TTTT checkForUniqueItem should use SdrModel& + pResultItem = static_cast<const XFillBitmapItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_LINEDASH: + { + pResultItem = static_cast<const XLineDashItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_LINESTART: + { + pResultItem = static_cast<const XLineStartItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_LINEEND: + { + pResultItem = static_cast<const XLineEndItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_FILLGRADIENT: + { + pResultItem = static_cast<const XFillGradientItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_FILLFLOATTRANSPARENCE: + { + // #85953# allow all kinds of XFillFloatTransparenceItem to be set + pResultItem = static_cast<const XFillFloatTransparenceItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_FILLHATCH: + { + pResultItem = static_cast<const XFillHatchItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + } + + // guarantee SfxItemSet existence + GetObjectItemSet(); + + if(pResultItem) + { + // force ItemSet + mxItemSet->Put(std::move(pResultItem)); + } + else + { + mxItemSet->Put(*pNewItem); + } + } + else + { + // clear item if ItemSet exists + if(HasSfxItemSet()) + { + mxItemSet->ClearItem(nWhich); + } + } + } + + void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool /*bBroadcast*/) + { + // guarantee SfxItemSet existence + GetObjectItemSet(); + + ImpRemoveStyleSheet(); + ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + + SdrObject& rObj = GetSdrObject(); + rObj.SetBoundRectDirty(); + rObj.SetBoundAndSnapRectsDirty(true); + } + + SfxStyleSheet* AttributeProperties::GetStyleSheet() const + { + return mpStyleSheet; + } + + void AttributeProperties::ForceStyleToHardAttributes() + { + if(!GetStyleSheet() || mpStyleSheet == nullptr) + return; + + // guarantee SfxItemSet existence + GetObjectItemSet(); + + // prepare copied, new itemset, but WITHOUT parent + SfxItemSet aDestItemSet(*mxItemSet); + aDestItemSet.SetParent(nullptr); + + // prepare forgetting the current stylesheet like in RemoveStyleSheet() + EndListening(*mpStyleSheet); + EndListening(*mpStyleSheet->GetPool()); + + // prepare the iter; use the mpObjectItemSet which may have less + // WhichIDs than the style. + SfxWhichIter aIter(aDestItemSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + const SfxPoolItem *pItem = nullptr; + + // now set all hard attributes of the current at the new itemset + while(nWhich) + { + // #i61284# use mpItemSet with parents, makes things easier and reduces to + // one loop + if(SfxItemState::SET == mxItemSet->GetItemState(nWhich, true, &pItem)) + { + aDestItemSet.Put(*pItem); + } + + nWhich = aIter.NextWhich(); + } + + // replace itemsets + mxItemSet.emplace(std::move(aDestItemSet)); + + // set necessary changes like in RemoveStyleSheet() + GetSdrObject().SetBoundRectDirty(); + GetSdrObject().SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + + mpStyleSheet = nullptr; + } + + void AttributeProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) + { + bool bHintUsed(false); + + const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint); + + if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet()) + { + SdrObject& rObj = GetSdrObject(); + //SdrPage* pPage = rObj.GetPage(); + + switch(pStyleHint->GetId()) + { + case SfxHintId::StyleSheetCreated : + { + // cannot happen, nothing to do + break; + } + case SfxHintId::StyleSheetModified : + case SfxHintId::StyleSheetChanged : + { + // notify change + break; + } + case SfxHintId::StyleSheetErased : + case SfxHintId::StyleSheetInDestruction : + { + // Style needs to be exchanged + SfxStyleSheet* pNewStSh = nullptr; + SdrModel& rModel(rObj.getSdrModelFromSdrObject()); + + // Do nothing if object is in destruction, else a StyleSheet may be found from + // a StyleSheetPool which is just being deleted itself. and thus it would be fatal + // to register as listener to that new StyleSheet. + if(!rObj.IsInDestruction()) + { + if(SfxStyleSheet* pStyleSheet = GetStyleSheet()) + { + pNewStSh = static_cast<SfxStyleSheet*>(rModel.GetStyleSheetPool()->Find( + pStyleSheet->GetParent(), pStyleSheet->GetFamily())); + } + + if(!pNewStSh) + { + pNewStSh = rModel.GetDefaultStyleSheet(); + } + } + + // remove used style, it's erased or in destruction + ImpRemoveStyleSheet(); + + if(pNewStSh) + { + ImpAddStyleSheet(pNewStSh, true); + } + + break; + } + default: break; + } + + // Get old BoundRect. Do this after the style change is handled + // in the ItemSet parts because GetBoundRect() may calculate a new + tools::Rectangle aBoundRect = rObj.GetLastBoundRect(); + + rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + + // tell the object about the change + rObj.SetChanged(); + rObj.BroadcastObjectChange(); + + //if(pPage && pPage->IsInserted()) + //{ + // rObj.BroadcastObjectChange(); + //} + + rObj.SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect); + + bHintUsed = true; + } + + if(!bHintUsed) + { + // forward to SdrObject ATM. Not sure if this will be necessary + // in the future. + GetSdrObject().Notify(rBC, rHint); + } + } + + bool AttributeProperties::isUsedByModel() const + { + const SdrObject& rObj(GetSdrObject()); + if (rObj.IsInserted()) + { + const SdrPage* const pPage(rObj.getSdrPageFromSdrObject()); + if (pPage && pPage->IsInserted()) + return true; + } + return false; + } + + void AttributeProperties::applyDefaultStyleSheetFromSdrModel() + { + SfxStyleSheet* pDefaultStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheet()); + + // tdf#118139 Only do this when StyleSheet really differs. It may e.g. + // be the case that nullptr == pDefaultStyleSheet and there is none set yet, + // so indeed no need to set it (needed for some strange old MSWord2003 + // documents with CustomShape-'Group' and added Text-Frames, see task description) + if(pDefaultStyleSheet != GetStyleSheet()) + { + // do not delete hard attributes when setting dsefault Style + SetStyleSheet(pDefaultStyleSheet, true, true); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |