blob: 60cc59d3973f3cef019b7c32790133714643ab5e [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"
#include <svx/sdr/properties/attributeproperties.hxx>
#include <svx/sdr/properties/itemsettools.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/xit.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/xlnasit.hxx>
#include <svx/xflasit.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdtrans.hxx>
#include <svx/svdpage.hxx>
// #114265#
#include <svl/smplhint.hxx>
//////////////////////////////////////////////////////////////////////////////
namespace sdr
{
namespace properties
{
void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet* pNewStyleSheet, sal_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)
{
mpStyleSheet = pNewStyleSheet;
// local ItemSet is needed here, force it
GetObjectItemSet();
// register as listener
StartListening(pNewStyleSheet->GetPool());
StartListening(*pNewStyleSheet);
// Delete hard attributes where items are set in the style sheet
if(!bDontRemoveHardAttr)
{
const SfxItemSet& rStyle = pNewStyleSheet->GetItemSet();
SfxWhichIter aIter(rStyle);
sal_uInt16 nWhich = aIter.FirstWhich();
while(nWhich)
{
if(SFX_ITEM_SET == rStyle.GetItemState(nWhich))
{
mpItemSet->ClearItem(nWhich);
}
nWhich = aIter.NextWhich();
}
}
// set new stylesheet as parent
mpItemSet->SetParent(&pNewStyleSheet->GetItemSet());
}
}
void AttributeProperties::ImpRemoveStyleSheet()
{
// Check type since it is destroyed when the type is deleted
if(GetStyleSheet() && HAS_BASE(SfxStyleSheet, mpStyleSheet))
{
EndListening(*mpStyleSheet);
EndListening(mpStyleSheet->GetPool());
// reset parent of ItemSet
if(mpItemSet)
{
mpItemSet->SetParent(0L);
}
SdrObject& rObj = GetSdrObject();
rObj.SetBoundRectDirty();
rObj.SetRectsDirty(sal_True);
}
mpStyleSheet = 0L;
}
// create a new itemset
SfxItemSet& AttributeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
{
return *(new SfxItemSet(rPool,
// ranges from SdrAttrObj
SDRATTR_START, SDRATTR_SHADOW_LAST,
SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
// end
0, 0));
}
AttributeProperties::AttributeProperties(SdrObject& rObj)
: DefaultProperties(rObj),
mpStyleSheet(0L)
{
}
AttributeProperties::AttributeProperties(const AttributeProperties& rProps, SdrObject& rObj)
: DefaultProperties(rProps, rObj),
mpStyleSheet(0L)
{
if(rProps.GetStyleSheet())
{
ImpAddStyleSheet(rProps.GetStyleSheet(), sal_True);
}
}
AttributeProperties::~AttributeProperties()
{
ImpRemoveStyleSheet();
}
BaseProperties& AttributeProperties::Clone(SdrObject& rObj) const
{
return *(new AttributeProperties(*this, rObj));
}
void AttributeProperties::ItemSetChanged(const SfxItemSet& /*rSet*/)
{
// own modifications
SdrObject& rObj = GetSdrObject();
rObj.SetBoundRectDirty();
rObj.SetRectsDirty(sal_True);
rObj.SetChanged();
}
void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
{
if(pNewItem)
{
const SfxPoolItem* pItem = pNewItem;
SdrModel* pModel = GetSdrObject().GetModel();
switch( nWhich )
{
case XATTR_FILLBITMAP:
{
pItem = ((XFillBitmapItem*)pItem)->checkForUniqueItem( pModel );
break;
}
case XATTR_LINEDASH:
{
pItem = ((XLineDashItem*)pItem)->checkForUniqueItem( pModel );
break;
}
case XATTR_LINESTART:
{
pItem = ((XLineStartItem*)pItem)->checkForUniqueItem( pModel );
break;
}
case XATTR_LINEEND:
{
pItem = ((XLineEndItem*)pItem)->checkForUniqueItem( pModel );
break;
}
case XATTR_FILLGRADIENT:
{
pItem = ((XFillGradientItem*)pItem)->checkForUniqueItem( pModel );
break;
}
case XATTR_FILLFLOATTRANSPARENCE:
{
// #85953# allow all kinds of XFillFloatTransparenceItem to be set
pItem = ((XFillFloatTransparenceItem*)pItem)->checkForUniqueItem( pModel );
break;
}
case XATTR_FILLHATCH:
{
pItem = ((XFillHatchItem*)pItem)->checkForUniqueItem( pModel );
break;
}
}
// set item
if(pItem)
{
// force ItemSet
GetObjectItemSet();
mpItemSet->Put(*pItem);
// delete item if it was a generated one
if(pItem != pNewItem)
{
delete (SfxPoolItem*)pItem;
}
}
}
else
{
// clear item if ItemSet exists
if(mpItemSet)
{
mpItemSet->ClearItem(nWhich);
}
}
}
void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, sal_Bool bDontRemoveHardAttr)
{
ImpRemoveStyleSheet();
ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
SdrObject& rObj = GetSdrObject();
rObj.SetBoundRectDirty();
rObj.SetRectsDirty(sal_True);
}
SfxStyleSheet* AttributeProperties::GetStyleSheet() const
{
return mpStyleSheet;
}
void AttributeProperties::MoveToItemPool(SfxItemPool* pSrcPool, SfxItemPool* pDestPool, SdrModel* pNewModel)
{
OSL_ASSERT(pNewModel!=NULL);
if(pSrcPool && pDestPool && (pSrcPool != pDestPool))
{
if(mpItemSet)
{
// migrate ItemSet to new pool. Scaling is NOT necessary
// because this functionality is used by UNDO only. Thus
// objects and ItemSets would be moved back to their original
// pool before usage.
SfxItemSet* pOldSet = mpItemSet;
SfxStyleSheet* pStySheet = GetStyleSheet();
if(pStySheet)
{
ImpRemoveStyleSheet();
}
mpItemSet = mpItemSet->Clone(sal_False, pDestPool);
GetSdrObject().GetModel()->MigrateItemSet(pOldSet, mpItemSet, pNewModel);
// set stylesheet (if used)
if(pStySheet)
{
// #i109515#
SfxItemPool* pStyleSheetPool = &pStySheet->GetPool().GetPool();
if(pStyleSheetPool == pDestPool)
{
// just re-set stylesheet
ImpAddStyleSheet(pStySheet, sal_True);
}
else
{
// StyleSheet is NOT from the correct pool.
// Look one up in the right pool with the same
// name or use the default.
// Look up the style in the new document.
OSL_ASSERT(pNewModel->GetStyleSheetPool() != NULL);
SfxStyleSheet* pNewStyleSheet = dynamic_cast<SfxStyleSheet*>(
pNewModel->GetStyleSheetPool()->Find(
pStySheet->GetName(),
SFX_STYLE_FAMILY_ALL));
if (pNewStyleSheet == NULL
|| &pNewStyleSheet->GetPool().GetPool() != pDestPool)
{
// There is no copy of the style in the new
// document. Use the default as a fallback.
pNewStyleSheet = pNewModel->GetDefaultStyleSheet();
}
ImpAddStyleSheet(pNewStyleSheet, sal_True);
}
}
delete pOldSet;
}
}
}
void AttributeProperties::SetModel(SdrModel* pOldModel, SdrModel* pNewModel)
{
if(pOldModel != pNewModel && pNewModel && !pNewModel->IsLoading())
{
// For a living model move the items from one pool to the other
if(pOldModel)
{
// If metric has changed, scale items.
MapUnit aOldUnit(pOldModel->GetScaleUnit());
MapUnit aNewUnit(pNewModel->GetScaleUnit());
sal_Bool bScaleUnitChanged(aNewUnit != aOldUnit);
Fraction aMetricFactor;
if(bScaleUnitChanged)
{
aMetricFactor = GetMapFactor(aOldUnit, aNewUnit).X();
Scale(aMetricFactor);
}
// Move all styles which are used by the object to the new
// StyleSheet pool
SfxStyleSheet* pOldStyleSheet = GetStyleSheet();
if(pOldStyleSheet)
{
SfxStyleSheetBase* pSheet = pOldStyleSheet;
SfxStyleSheetBasePool* pOldPool = pOldModel->GetStyleSheetPool();
SfxStyleSheetBasePool* pNewPool = pNewModel->GetStyleSheetPool();
DBG_ASSERT(pOldPool, "Properties::SetModel(): Object has StyleSheet but no StyleSheetPool (!)");
if(pOldPool && pNewPool)
{
// build a list of to-be-copied Styles
List aList;
SfxStyleSheetBase* pAnchor = 0L;
while(pSheet)
{
pAnchor = pNewPool->Find(pSheet->GetName(), pSheet->GetFamily());
if(!pAnchor)
{
aList.Insert(pSheet, LIST_APPEND);
pSheet = pOldPool->Find(pSheet->GetParent(), pSheet->GetFamily());
}
else
{
// the style does exist
pSheet = 0L;
}
}
// copy and set the parents
pSheet = (SfxStyleSheetBase*)aList.First();
SfxStyleSheetBase* pNewSheet = 0L;
SfxStyleSheetBase* pLastSheet = 0L;
SfxStyleSheetBase* pForThisObject = 0L;
while(pSheet)
{
pNewSheet = &pNewPool->Make(pSheet->GetName(), pSheet->GetFamily(), pSheet->GetMask());
pNewSheet->GetItemSet().Put(pSheet->GetItemSet(), sal_False);
if(bScaleUnitChanged)
{
sdr::properties::ScaleItemSet(pNewSheet->GetItemSet(), aMetricFactor);
}
if(pLastSheet)
{
pLastSheet->SetParent(pNewSheet->GetName());
}
if(!pForThisObject)
{
pForThisObject = pNewSheet;
}
pLastSheet = pNewSheet;
pSheet = (SfxStyleSheetBase*)aList.Next();
}
// Set link to the Style found in the Pool
if(pAnchor && pLastSheet)
{
pLastSheet->SetParent(pAnchor->GetName());
}
// if list was empty (all Styles exist in destination pool)
// pForThisObject is not yet set
if(!pForThisObject && pAnchor)
{
pForThisObject = pAnchor;
}
// De-register at old and register at new Style
if(GetStyleSheet() != pForThisObject)
{
ImpRemoveStyleSheet();
ImpAddStyleSheet((SfxStyleSheet*)pForThisObject, sal_True);
}
}
else
{
// there is no StyleSheetPool in the new model, thus set
// all items as hard items in the object
List aList;
const SfxItemSet* pItemSet = &pOldStyleSheet->GetItemSet();
while(pItemSet)
{
aList.Insert((void*)pItemSet, CONTAINER_APPEND);
pItemSet = pItemSet->GetParent();
}
SfxItemSet* pNewSet = &CreateObjectSpecificItemSet(pNewModel->GetItemPool());
pItemSet = (SfxItemSet*)aList.Last();
while(pItemSet)
{
pNewSet->Put(*pItemSet);
pItemSet = (SfxItemSet*)aList.Prev();
}
// Items which were hard attributes before need to stay
if(mpItemSet)
{
SfxWhichIter aIter(*mpItemSet);
sal_uInt16 nWhich = aIter.FirstWhich();
while(nWhich)
{
if(mpItemSet->GetItemState(nWhich, sal_False) == SFX_ITEM_SET)
{
pNewSet->Put(mpItemSet->Get(nWhich));
}
nWhich = aIter.NextWhich();
}
}
if(bScaleUnitChanged)
{
ScaleItemSet(*pNewSet, aMetricFactor);
}
if(mpItemSet)
{
if(GetStyleSheet())
{
ImpRemoveStyleSheet();
}
delete mpItemSet;
mpItemSet = 0L;
}
mpItemSet = pNewSet;
}
}
}
// each object gets the default Style if there is none set yet.
if(!GetStyleSheet() && pNewModel && !pNewModel->IsLoading())
{
GetObjectItemSet(); // #118414# force ItemSet to allow style to be set
SetStyleSheet(pNewModel->GetDefaultStyleSheet(), sal_True);
}
}
}
void AttributeProperties::ForceStyleToHardAttributes()
{
if(GetStyleSheet() && HAS_BASE(SfxStyleSheet, mpStyleSheet))
{
// prepare copied, new itemset, but WITHOUT parent
GetObjectItemSet();
SfxItemSet* pDestItemSet = new SfxItemSet(*mpItemSet);
pDestItemSet->SetParent(0L);
// pepare 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(*pDestItemSet);
sal_uInt16 nWhich(aIter.FirstWhich());
const SfxPoolItem *pItem = NULL;
// 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(SFX_ITEM_SET == mpItemSet->GetItemState(nWhich, true, &pItem))
{
pDestItemSet->Put(*pItem);
}
nWhich = aIter.NextWhich();
}
// replace itemsets
delete mpItemSet;
mpItemSet = pDestItemSet;
// set necessary changes like in RemoveStyleSheet()
GetSdrObject().SetBoundRectDirty();
GetSdrObject().SetRectsDirty(sal_True);
mpStyleSheet = NULL;
}
}
void AttributeProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{
sal_Bool bHintUsed(sal_False);
SfxStyleSheetHint *pStyleHint = PTR_CAST(SfxStyleSheetHint, &rHint);
if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet())
{
SdrObject& rObj = GetSdrObject();
//SdrPage* pPage = rObj.GetPage();
switch(pStyleHint->GetHint())
{
case SFX_STYLESHEET_CREATED :
{
// cannot happen, nothing to do
break;
}
case SFX_STYLESHEET_MODIFIED :
case SFX_STYLESHEET_CHANGED :
{
// notify change
break;
}
case SFX_STYLESHEET_ERASED :
case SFX_STYLESHEET_INDESTRUCTION :
{
// Style needs to be exchanged
SfxStyleSheet* pNewStSh = 0L;
SdrModel* pModel = rObj.GetModel();
// #111111#
// 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(pModel && !rObj.IsInDestruction())
{
if(HAS_BASE(SfxStyleSheet, GetStyleSheet()))
{
pNewStSh = (SfxStyleSheet*)pModel->GetStyleSheetPool()->Find(
GetStyleSheet()->GetParent(), GetStyleSheet()->GetFamily());
}
if(!pNewStSh)
{
pNewStSh = pModel->GetDefaultStyleSheet();
}
}
// remove used style, it's erased or in destruction
ImpRemoveStyleSheet();
if(pNewStSh)
{
ImpAddStyleSheet(pNewStSh, sal_True);
}
break;
}
}
// Get old BoundRect. Do this after the style change is handled
// in the ItemSet parts because GetBoundRect() may calculate a new
Rectangle aBoundRect = rObj.GetLastBoundRect();
rObj.SetRectsDirty(sal_True);
// tell the object about the change
rObj.SetChanged();
rObj.BroadcastObjectChange();
//if(pPage && pPage->IsInserted())
//{
// rObj.BroadcastObjectChange();
//}
rObj.SendUserCall(SDRUSERCALL_CHGATTR, aBoundRect);
bHintUsed = sal_True;
}
if(!bHintUsed)
{
// forward to SdrObject ATM. Not sure if this will be necessary
// in the future.
GetSdrObject().Notify(rBC, rHint);
}
}
} // end of namespace properties
} // end of namespace sdr
//////////////////////////////////////////////////////////////////////////////
// eof