1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 #include <svx/sdr/properties/attributeproperties.hxx>
31 #include <svx/sdr/properties/itemsettools.hxx>
32 #include <tools/debug.hxx>
33 #include <svl/itemset.hxx>
34 #include <svl/style.hxx>
35 #include <svl/whiter.hxx>
36 #include <svl/poolitem.hxx>
37 #include <svx/svdobj.hxx>
38 #include <svx/svddef.hxx>
39 #include <svx/xit.hxx>
40 #include <svx/xbtmpit.hxx>
41 #include <svx/xlndsit.hxx>
42 #include <svx/xlnstit.hxx>
43 #include <svx/xlnedit.hxx>
44 #include <svx/xflgrit.hxx>
45 #include <svx/xflftrit.hxx>
46 #include <svx/xflhtit.hxx>
47 #include <svx/xlnasit.hxx>
48 #include <svx/xflasit.hxx>
49 #include <svx/svdmodel.hxx>
50 #include <svx/svdtrans.hxx>
51 #include <svx/svdpage.hxx>
52 
53 // #114265#
54 #include <svl/smplhint.hxx>
55 
56 //////////////////////////////////////////////////////////////////////////////
57 
58 namespace sdr
59 {
60 	namespace properties
61 	{
62 		void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet* pNewStyleSheet, sal_Bool bDontRemoveHardAttr)
63 		{
64 			// test if old StyleSheet is cleared, else it would be lost
65 			// after this method -> memory leak (!)
66 			DBG_ASSERT(!mpStyleSheet, "Old style sheet not deleted before setting new one (!)");
67 
68 			if(pNewStyleSheet)
69 			{
70 				mpStyleSheet = pNewStyleSheet;
71 
72 				// local ItemSet is needed here, force it
73 				GetObjectItemSet();
74 
75 				// register as listener
76 				StartListening(pNewStyleSheet->GetPool());
77 				StartListening(*pNewStyleSheet);
78 
79 				// Delete hard attributes where items are set in the style sheet
80 				if(!bDontRemoveHardAttr)
81 				{
82 					const SfxItemSet& rStyle = pNewStyleSheet->GetItemSet();
83 					SfxWhichIter aIter(rStyle);
84 					sal_uInt16 nWhich = aIter.FirstWhich();
85 
86 					while(nWhich)
87 					{
88 						if(SFX_ITEM_SET == rStyle.GetItemState(nWhich))
89 						{
90 							mpItemSet->ClearItem(nWhich);
91 						}
92 
93 						nWhich = aIter.NextWhich();
94 					}
95 				}
96 
97 				// set new stylesheet as parent
98 				mpItemSet->SetParent(&pNewStyleSheet->GetItemSet());
99 			}
100 		}
101 
102 		void AttributeProperties::ImpRemoveStyleSheet()
103 		{
104 			// Check type since it is destroyed when the type is deleted
105 			if(GetStyleSheet() && HAS_BASE(SfxStyleSheet, mpStyleSheet))
106 			{
107 				EndListening(*mpStyleSheet);
108 				EndListening(mpStyleSheet->GetPool());
109 
110 				// reset parent of ItemSet
111 				if(mpItemSet)
112 				{
113 					mpItemSet->SetParent(0L);
114 				}
115 
116 				SdrObject& rObj = GetSdrObject();
117 				rObj.SetBoundRectDirty();
118 				rObj.SetRectsDirty(sal_True);
119 			}
120 
121 			mpStyleSheet = 0L;
122 		}
123 
124 		// create a new itemset
125 		SfxItemSet& AttributeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
126 		{
127 			return *(new SfxItemSet(rPool,
128 
129 				// ranges from SdrAttrObj
130 				SDRATTR_START, SDRATTR_SHADOW_LAST,
131 				SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
132 				SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
133 
134 				// end
135 				0, 0));
136 		}
137 
138 		AttributeProperties::AttributeProperties(SdrObject& rObj)
139 		:	DefaultProperties(rObj),
140 			mpStyleSheet(0L)
141 		{
142 		}
143 
144 		AttributeProperties::AttributeProperties(const AttributeProperties& rProps, SdrObject& rObj)
145 		:	DefaultProperties(rProps, rObj),
146 			mpStyleSheet(0L)
147 		{
148 			if(rProps.GetStyleSheet())
149 			{
150 				ImpAddStyleSheet(rProps.GetStyleSheet(), sal_True);
151 			}
152 		}
153 
154 		AttributeProperties::~AttributeProperties()
155 		{
156 			ImpRemoveStyleSheet();
157 		}
158 
159 		BaseProperties& AttributeProperties::Clone(SdrObject& rObj) const
160 		{
161 			return *(new AttributeProperties(*this, rObj));
162 		}
163 
164 		void AttributeProperties::ItemSetChanged(const SfxItemSet& /*rSet*/)
165 		{
166 			// own modifications
167 			SdrObject& rObj = GetSdrObject();
168 
169 			rObj.SetBoundRectDirty();
170 			rObj.SetRectsDirty(sal_True);
171 			rObj.SetChanged();
172 		}
173 
174 		void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
175 		{
176 			if(pNewItem)
177 			{
178 				const SfxPoolItem* pItem = pNewItem;
179 				SdrModel* pModel = GetSdrObject().GetModel();
180 
181 				switch( nWhich )
182 				{
183 					case XATTR_FILLBITMAP:
184 					{
185 						pItem = ((XFillBitmapItem*)pItem)->checkForUniqueItem( pModel );
186 						break;
187 					}
188 					case XATTR_LINEDASH:
189 					{
190 						pItem = ((XLineDashItem*)pItem)->checkForUniqueItem( pModel );
191 						break;
192 					}
193 					case XATTR_LINESTART:
194 					{
195 						pItem = ((XLineStartItem*)pItem)->checkForUniqueItem( pModel );
196 						break;
197 					}
198 					case XATTR_LINEEND:
199 					{
200 						pItem = ((XLineEndItem*)pItem)->checkForUniqueItem( pModel );
201 						break;
202 					}
203 					case XATTR_FILLGRADIENT:
204 					{
205 						pItem = ((XFillGradientItem*)pItem)->checkForUniqueItem( pModel );
206 						break;
207 					}
208 					case XATTR_FILLFLOATTRANSPARENCE:
209 					{
210 						// #85953# allow all kinds of XFillFloatTransparenceItem to be set
211 						pItem = ((XFillFloatTransparenceItem*)pItem)->checkForUniqueItem( pModel );
212 						break;
213 					}
214 					case XATTR_FILLHATCH:
215 					{
216 						pItem = ((XFillHatchItem*)pItem)->checkForUniqueItem( pModel );
217 						break;
218 					}
219 				}
220 
221 				// set item
222 				if(pItem)
223 				{
224 					// force ItemSet
225 					GetObjectItemSet();
226 					mpItemSet->Put(*pItem);
227 
228 					// delete item if it was a generated one
229 					if(pItem != pNewItem)
230 					{
231 						delete (SfxPoolItem*)pItem;
232 					}
233 				}
234 			}
235 			else
236 			{
237 				// clear item if ItemSet exists
238 				if(mpItemSet)
239 				{
240 					mpItemSet->ClearItem(nWhich);
241 				}
242 			}
243 		}
244 
245 		void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, sal_Bool bDontRemoveHardAttr)
246 		{
247 			ImpRemoveStyleSheet();
248 			ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
249 
250 			SdrObject& rObj = GetSdrObject();
251 			rObj.SetBoundRectDirty();
252 			rObj.SetRectsDirty(sal_True);
253 		}
254 
255 		SfxStyleSheet* AttributeProperties::GetStyleSheet() const
256 		{
257 			return mpStyleSheet;
258 		}
259 
260 		void AttributeProperties::MoveToItemPool(SfxItemPool* pSrcPool, SfxItemPool* pDestPool, SdrModel* pNewModel)
261 		{
262             OSL_ASSERT(pNewModel!=NULL);
263 
264 			if(pSrcPool && pDestPool && (pSrcPool != pDestPool))
265 			{
266 				if(mpItemSet)
267 				{
268 					// migrate ItemSet to new pool. Scaling is NOT necessary
269 					// because this functionality is used by UNDO only. Thus
270 					// objects and ItemSets would be moved back to their original
271 					// pool before usage.
272 					SfxItemSet* pOldSet = mpItemSet;
273 					SfxStyleSheet* pStySheet = GetStyleSheet();
274 
275 					if(pStySheet)
276 					{
277 						ImpRemoveStyleSheet();
278 					}
279 
280 					mpItemSet = mpItemSet->Clone(sal_False, pDestPool);
281 					GetSdrObject().GetModel()->MigrateItemSet(pOldSet, mpItemSet, pNewModel);
282 
283 					// set stylesheet (if used)
284 					if(pStySheet)
285 					{
286                         // #i109515#
287                         SfxItemPool* pStyleSheetPool = &pStySheet->GetPool().GetPool();
288 
289                         if(pStyleSheetPool == pDestPool)
290                         {
291                             // just re-set stylesheet
292     						ImpAddStyleSheet(pStySheet, sal_True);
293                         }
294                         else
295                         {
296                             // StyleSheet is NOT from the correct pool.
297                             // Look one up in the right pool with the same
298                             // name or use the default.
299 
300                             // Look up the style in the new document.
301                             OSL_ASSERT(pNewModel->GetStyleSheetPool() != NULL);
302                             SfxStyleSheet* pNewStyleSheet = dynamic_cast<SfxStyleSheet*>(
303                                 pNewModel->GetStyleSheetPool()->Find(
304                                     pStySheet->GetName(),
305                                     SFX_STYLE_FAMILY_ALL));
306                             if (pNewStyleSheet == NULL
307                                 || &pNewStyleSheet->GetPool().GetPool() != pDestPool)
308                             {
309                                 // There is no copy of the style in the new
310                                 // document.  Use the default as a fallback.
311                                 pNewStyleSheet = pNewModel->GetDefaultStyleSheet();
312                             }
313     						ImpAddStyleSheet(pNewStyleSheet, sal_True);
314                         }
315 					}
316 
317 					delete pOldSet;
318 				}
319 			}
320 		}
321 
322 		void AttributeProperties::SetModel(SdrModel* pOldModel, SdrModel* pNewModel)
323 		{
324 			if(pOldModel != pNewModel && pNewModel && !pNewModel->IsLoading())
325 			{
326 				// For a living model move the items from one pool to the other
327 				if(pOldModel)
328 				{
329 					// If metric has changed, scale items.
330 					MapUnit aOldUnit(pOldModel->GetScaleUnit());
331 					MapUnit aNewUnit(pNewModel->GetScaleUnit());
332 					sal_Bool bScaleUnitChanged(aNewUnit != aOldUnit);
333 					Fraction aMetricFactor;
334 
335 					if(bScaleUnitChanged)
336 					{
337 						aMetricFactor = GetMapFactor(aOldUnit, aNewUnit).X();
338 						Scale(aMetricFactor);
339 					}
340 
341 					// Move all styles which are used by the object to the new
342 					// StyleSheet pool
343 					SfxStyleSheet* pOldStyleSheet = GetStyleSheet();
344 
345 					if(pOldStyleSheet)
346 					{
347 						SfxStyleSheetBase* pSheet = pOldStyleSheet;
348 						SfxStyleSheetBasePool* pOldPool = pOldModel->GetStyleSheetPool();
349 						SfxStyleSheetBasePool* pNewPool = pNewModel->GetStyleSheetPool();
350 						DBG_ASSERT(pOldPool, "Properties::SetModel(): Object has StyleSheet but no StyleSheetPool (!)");
351 
352 						if(pOldPool && pNewPool)
353 						{
354 							// build a list of to-be-copied Styles
355 							List aList;
356 							SfxStyleSheetBase* pAnchor = 0L;
357 
358 							while(pSheet)
359 							{
360 								pAnchor = pNewPool->Find(pSheet->GetName(), pSheet->GetFamily());
361 
362 								if(!pAnchor)
363 								{
364 									aList.Insert(pSheet, LIST_APPEND);
365 									pSheet = pOldPool->Find(pSheet->GetParent(), pSheet->GetFamily());
366 								}
367 								else
368 								{
369 									// the style does exist
370 									pSheet = 0L;
371 								}
372 							}
373 
374 							// copy and set the parents
375 							pSheet = (SfxStyleSheetBase*)aList.First();
376 							SfxStyleSheetBase* pNewSheet = 0L;
377 							SfxStyleSheetBase* pLastSheet = 0L;
378 							SfxStyleSheetBase* pForThisObject = 0L;
379 
380 							while(pSheet)
381 							{
382 								pNewSheet = &pNewPool->Make(pSheet->GetName(), pSheet->GetFamily(), pSheet->GetMask());
383 								pNewSheet->GetItemSet().Put(pSheet->GetItemSet(), sal_False);
384 
385 								if(bScaleUnitChanged)
386 								{
387 									sdr::properties::ScaleItemSet(pNewSheet->GetItemSet(), aMetricFactor);
388 								}
389 
390 								if(pLastSheet)
391 								{
392 									pLastSheet->SetParent(pNewSheet->GetName());
393 								}
394 
395 								if(!pForThisObject)
396 								{
397 									pForThisObject = pNewSheet;
398 								}
399 
400 								pLastSheet = pNewSheet;
401 								pSheet = (SfxStyleSheetBase*)aList.Next();
402 							}
403 
404 							// Set link to the Style found in the Pool
405 							if(pAnchor && pLastSheet)
406 							{
407 								pLastSheet->SetParent(pAnchor->GetName());
408 							}
409 
410 							// if list was empty (all Styles exist in destination pool)
411 							// pForThisObject is not yet set
412 							if(!pForThisObject && pAnchor)
413 							{
414 								pForThisObject = pAnchor;
415 							}
416 
417 							// De-register at old and register at new Style
418 							if(GetStyleSheet() != pForThisObject)
419 							{
420 								ImpRemoveStyleSheet();
421 								ImpAddStyleSheet((SfxStyleSheet*)pForThisObject, sal_True);
422 							}
423 						}
424 						else
425 						{
426 							// there is no StyleSheetPool in the new model, thus set
427 							// all items as hard items in the object
428 							List aList;
429 							const SfxItemSet* pItemSet = &pOldStyleSheet->GetItemSet();
430 
431 							while(pItemSet)
432 							{
433 								aList.Insert((void*)pItemSet, CONTAINER_APPEND);
434 								pItemSet = pItemSet->GetParent();
435 							}
436 
437 							SfxItemSet* pNewSet = &CreateObjectSpecificItemSet(pNewModel->GetItemPool());
438 							pItemSet = (SfxItemSet*)aList.Last();
439 
440 							while(pItemSet)
441 							{
442 								pNewSet->Put(*pItemSet);
443 								pItemSet = (SfxItemSet*)aList.Prev();
444 							}
445 
446 							// Items which were hard attributes before need to stay
447 							if(mpItemSet)
448 							{
449 								SfxWhichIter aIter(*mpItemSet);
450 								sal_uInt16 nWhich = aIter.FirstWhich();
451 
452 								while(nWhich)
453 								{
454 									if(mpItemSet->GetItemState(nWhich, sal_False) == SFX_ITEM_SET)
455 									{
456 										pNewSet->Put(mpItemSet->Get(nWhich));
457 									}
458 
459 									nWhich = aIter.NextWhich();
460 								}
461 							}
462 
463 							if(bScaleUnitChanged)
464 							{
465 								ScaleItemSet(*pNewSet, aMetricFactor);
466 							}
467 
468 							if(mpItemSet)
469 							{
470 								if(GetStyleSheet())
471 								{
472 									ImpRemoveStyleSheet();
473 								}
474 
475 								delete mpItemSet;
476 								mpItemSet = 0L;
477 							}
478 
479 							mpItemSet = pNewSet;
480 						}
481 					}
482 				}
483 
484 				// each object gets the default Style if there is none set yet.
485 				if(!GetStyleSheet() && pNewModel && !pNewModel->IsLoading())
486 				{
487 				    GetObjectItemSet(); // #118414# force ItemSet to allow style to be set
488                     SetStyleSheet(pNewModel->GetDefaultStyleSheet(), sal_True);
489 				}
490 			}
491 		}
492 
493 		void AttributeProperties::ForceStyleToHardAttributes()
494 		{
495 			if(GetStyleSheet() && HAS_BASE(SfxStyleSheet, mpStyleSheet))
496 			{
497 				// prepare copied, new itemset, but WITHOUT parent
498 				GetObjectItemSet();
499 				SfxItemSet* pDestItemSet = new SfxItemSet(*mpItemSet);
500 				pDestItemSet->SetParent(0L);
501 
502 				// pepare forgetting the current stylesheet like in RemoveStyleSheet()
503 				EndListening(*mpStyleSheet);
504 				EndListening(mpStyleSheet->GetPool());
505 
506 				// prepare the iter; use the mpObjectItemSet which may have less
507 				// WhichIDs than the style.
508 				SfxWhichIter aIter(*pDestItemSet);
509 				sal_uInt16 nWhich(aIter.FirstWhich());
510 				const SfxPoolItem *pItem = NULL;
511 
512 				// now set all hard attributes of the current at the new itemset
513 				while(nWhich)
514 				{
515 					// #i61284# use mpItemSet with parents, makes things easier and reduces to
516 					// one loop
517 					if(SFX_ITEM_SET == mpItemSet->GetItemState(nWhich, true, &pItem))
518 					{
519 						pDestItemSet->Put(*pItem);
520 					}
521 
522 					nWhich = aIter.NextWhich();
523 				}
524 
525 				// replace itemsets
526 				delete mpItemSet;
527 				mpItemSet = pDestItemSet;
528 
529 				// set necessary changes like in RemoveStyleSheet()
530 				GetSdrObject().SetBoundRectDirty();
531 				GetSdrObject().SetRectsDirty(sal_True);
532 
533 				mpStyleSheet = NULL;
534 			}
535 		}
536 
537 		void AttributeProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
538 		{
539 			sal_Bool bHintUsed(sal_False);
540 
541 			SfxStyleSheetHint *pStyleHint = PTR_CAST(SfxStyleSheetHint, &rHint);
542 
543 			if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet())
544 			{
545 				SdrObject& rObj = GetSdrObject();
546 				//SdrPage* pPage = rObj.GetPage();
547 
548 				switch(pStyleHint->GetHint())
549 				{
550 					case SFX_STYLESHEET_CREATED			:
551 					{
552 						// cannot happen, nothing to do
553 						break;
554 					}
555 					case SFX_STYLESHEET_MODIFIED		:
556 					case SFX_STYLESHEET_CHANGED			:
557 					{
558 						// notify change
559 						break;
560 					}
561 					case SFX_STYLESHEET_ERASED			:
562 					case SFX_STYLESHEET_INDESTRUCTION	:
563 					{
564 						// Style needs to be exchanged
565 						SfxStyleSheet* pNewStSh = 0L;
566 						SdrModel* pModel = rObj.GetModel();
567 
568 						// #111111#
569 						// Do nothing if object is in destruction, else a StyleSheet may be found from
570 						// a StyleSheetPool which is just being deleted itself. and thus it would be fatal
571 						// to register as listener to that new StyleSheet.
572 						if(pModel && !rObj.IsInDestruction())
573 						{
574 							if(HAS_BASE(SfxStyleSheet, GetStyleSheet()))
575 							{
576 								pNewStSh = (SfxStyleSheet*)pModel->GetStyleSheetPool()->Find(
577 									GetStyleSheet()->GetParent(), GetStyleSheet()->GetFamily());
578 							}
579 
580 							if(!pNewStSh)
581 							{
582 								pNewStSh = pModel->GetDefaultStyleSheet();
583 							}
584 						}
585 
586 						// remove used style, it's erased or in destruction
587 						ImpRemoveStyleSheet();
588 
589 						if(pNewStSh)
590 						{
591 							ImpAddStyleSheet(pNewStSh, sal_True);
592 						}
593 
594 						break;
595 					}
596 				}
597 
598 				// Get old BoundRect. Do this after the style change is handled
599 				// in the ItemSet parts because GetBoundRect() may calculate a new
600 				Rectangle aBoundRect = rObj.GetLastBoundRect();
601 
602 				rObj.SetRectsDirty(sal_True);
603 
604 				// tell the object about the change
605 				rObj.SetChanged();
606 				rObj.BroadcastObjectChange();
607 
608 				//if(pPage && pPage->IsInserted())
609 				//{
610 				//	rObj.BroadcastObjectChange();
611 				//}
612 
613 				rObj.SendUserCall(SDRUSERCALL_CHGATTR, aBoundRect);
614 
615 				bHintUsed = sal_True;
616 			}
617 
618 			if(!bHintUsed)
619 			{
620 				// forward to SdrObject ATM. Not sure if this will be necessary
621 				// in the future.
622 				GetSdrObject().Notify(rBC, rHint);
623 			}
624 		}
625 	} // end of namespace properties
626 } // end of namespace sdr
627 
628 //////////////////////////////////////////////////////////////////////////////
629 // eof
630