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_slideshow.hxx"
30 
31 // must be first
32 #include <canvas/debug.hxx>
33 #include <tools/diagnose_ex.h>
34 #include <canvas/verbosetrace.hxx>
35 
36 #include <rtl/logfile.hxx>
37 #include <osl/diagnose.hxx>
38 #include <com/sun/star/awt/Rectangle.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/awt/FontWeight.hpp>
41 #include <comphelper/anytostring.hxx>
42 #include <cppuhelper/exc_hlp.hxx>
43 
44 #include <vcl/metaact.hxx>
45 #include <vcl/gdimtf.hxx>
46 #include <vcl/wrkwin.hxx>
47 
48 #include <basegfx/numeric/ftools.hxx>
49 #include <basegfx/range/rangeexpander.hxx>
50 
51 #include <rtl/math.hxx>
52 
53 #include <com/sun/star/drawing/TextAnimationKind.hpp>
54 
55 #include <vcl/svapp.hxx>
56 #include <vcl/window.hxx>
57 #include <tools/stream.hxx>
58 #include <com/sun/star/frame/XModel.hpp>
59 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
60 #include <com/sun/star/datatransfer/XTransferable.hpp>
61 
62 #include <comphelper/scopeguard.hxx>
63 #include <canvas/canvastools.hxx>
64 
65 #include <cmath> // for trigonometry and fabs
66 #include <algorithm>
67 #include <functional>
68 #include <limits>
69 
70 #include "drawshapesubsetting.hxx"
71 #include "drawshape.hxx"
72 #include "eventqueue.hxx"
73 #include "wakeupevent.hxx"
74 #include "subsettableshapemanager.hxx"
75 #include "intrinsicanimationactivity.hxx"
76 #include "slideshowexceptions.hxx"
77 #include "tools.hxx"
78 #include "gdimtftools.hxx"
79 #include "drawinglayeranimation.hxx"
80 
81 #include <boost/bind.hpp>
82 #include <math.h>
83 
84 using namespace ::com::sun::star;
85 
86 
87 namespace slideshow
88 {
89     namespace internal
90     {
91         //#i75867# poor quality of ole's alternative view with 3D scenes and zoomfactors besides 100%
92         //metafiles are resolution dependent when bitmaps are contained with is the case for 3D scenes for example
93         //in addition a chart has resolution dependent content as it might skip points that are not visible for a given resolution (this is done for performance reasons)
94         bool local_getMetafileForChart( const uno::Reference< lang::XComponent >& 	  xSource,
95                   const uno::Reference< drawing::XDrawPage >&     xContainingPage,
96                   GDIMetaFile&                                    rMtf )
97         {
98             //get the chart model
99             uno::Reference< beans::XPropertySet > xPropSet( xSource, uno::UNO_QUERY );
100             uno::Reference< frame::XModel > xChartModel;
101             getPropertyValue( xChartModel, xPropSet, OUSTR("Model"));
102             uno::Reference< lang::XMultiServiceFactory > xFact( xChartModel, uno::UNO_QUERY );
103             OSL_ENSURE( xFact.is(), "Chart cannot be painted pretty!\n" );
104             if(!xFact.is())
105                 return false;
106 
107             //get the chart view
108             uno::Reference< datatransfer::XTransferable > xChartViewTransferable(
109                 xFact->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.chart2.ChartView" ) ) ), uno::UNO_QUERY );
110             uno::Reference< beans::XPropertySet > xChartViewProp( xChartViewTransferable, uno::UNO_QUERY );
111             OSL_ENSURE( xChartViewProp.is(), "Chart cannot be painted pretty!\n" );
112             if( !xChartViewProp.is() )
113                 return false;
114 
115             //estimate zoom and resolution (this is only a workaround, correct would be to know and use the exact zoom and resoltion during slideshow display)
116             sal_Int32 nScaleXNumerator = 100;//zoom factor -> exact values are important for the quality of the created bitmap especially for 3D charts
117             sal_Int32 nScaleYNumerator = 100;
118             sal_Int32 nScaleXDenominator = 100;
119             sal_Int32 nScaleYDenominator = 100;
120             awt::Size aPixelPerChart( 1000, 1000 );//when data points happen to be on the same pixel as their predecessor no shape is created to safe performance
121 
122             Window* pActiveTopWindow( Application::GetActiveTopWindow() );
123             WorkWindow* pWorkWindow( dynamic_cast<WorkWindow*>(pActiveTopWindow));
124             if( pWorkWindow && pWorkWindow->IsPresentationMode() )
125             {
126                 Size aPixScreenSize( pActiveTopWindow->GetOutputSizePixel() );
127                 aPixelPerChart = awt::Size( aPixScreenSize.getWidth(), aPixScreenSize.getHeight() );//this is still to much (but costs only seldom performance), correct would be pixel per chart object
128 
129                 uno::Reference< beans::XPropertySet > xPageProp( xContainingPage, uno::UNO_QUERY );
130                 sal_Int32 nLogicPageWidth=1;
131                 sal_Int32 nLogicPageHeight=1;
132                 if( getPropertyValue( nLogicPageWidth, xPageProp, OUSTR("Width")) &&
133                     getPropertyValue( nLogicPageHeight, xPageProp, OUSTR("Height")) )
134                 {
135                     Size aLogicScreenSize( pActiveTopWindow->PixelToLogic( aPixScreenSize, MAP_100TH_MM ) );
136                     nScaleXNumerator = aLogicScreenSize.getWidth();
137                     nScaleYNumerator = aLogicScreenSize.getHeight();
138                     nScaleXDenominator = nLogicPageWidth;
139                     nScaleYDenominator = nLogicPageHeight;
140                 }
141             }
142             else
143             {
144                 long nMaxPixWidth = 0;
145                 long nMaxPixHeight = 0;
146                 unsigned int nScreenCount( Application::GetScreenCount() );
147                 for( unsigned int nScreen=0; nScreen<nScreenCount; nScreen++ )
148                 {
149                     Rectangle aCurScreenRect( Application::GetScreenPosSizePixel( nScreen ) );
150                     if( aCurScreenRect.GetWidth() > nMaxPixWidth )
151                         nMaxPixWidth = aCurScreenRect.GetWidth();
152                     if( aCurScreenRect.GetHeight() > nMaxPixHeight )
153                         nMaxPixHeight = aCurScreenRect.GetHeight();
154                 }
155                 if(nMaxPixWidth>1 && nMaxPixHeight>1)
156                     aPixelPerChart = awt::Size( nMaxPixWidth, nMaxPixHeight );//this is still to much (but costs only seldom performance), correct would be pixel per chart object
157             }
158 
159             try
160             {
161                 uno::Sequence< beans::PropertyValue > aZoomFactors(4);
162                 aZoomFactors[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleXNumerator") );
163                 aZoomFactors[0].Value = uno::makeAny( nScaleXNumerator );
164                 aZoomFactors[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleXDenominator") );
165                 aZoomFactors[1].Value = uno::makeAny( nScaleXDenominator );
166                 aZoomFactors[2].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleYNumerator") );
167                 aZoomFactors[2].Value = uno::makeAny( nScaleYNumerator );
168                 aZoomFactors[3].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ScaleYDenominator") );
169                 aZoomFactors[3].Value = uno::makeAny( nScaleYDenominator );
170 
171                 xChartViewProp->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ZoomFactors") ), uno::makeAny( aZoomFactors ));
172                 xChartViewProp->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Resolution") ), uno::makeAny( aPixelPerChart ));
173             }
174             catch (uno::Exception &)
175             {
176                 OSL_ENSURE( false, rtl::OUStringToOString(
177                                 comphelper::anyToString(
178                                     cppu::getCaughtException() ),
179                                 RTL_TEXTENCODING_UTF8 ).getStr() );
180             }
181 
182             //get a metafile from the prepared chart view
183             datatransfer::DataFlavor aDataFlavor(
184                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"") ),
185                     ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GDIMetaFile" ) ),
186                     ::getCppuType( (const uno::Sequence< sal_Int8 >*) 0 ) );
187             uno::Any aData( xChartViewTransferable->getTransferData( aDataFlavor ) );
188             uno::Sequence< sal_Int8 > aSeq;
189             if( aData >>= aSeq )
190             {
191 		        ::std::auto_ptr< SvMemoryStream > pSrcStm( new SvMemoryStream( (char*) aSeq.getConstArray(), aSeq.getLength(), STREAM_WRITE | STREAM_TRUNC ) );
192 	            *(pSrcStm.get() ) >> rMtf;
193                 return true;
194             }
195             return false;
196         }
197 
198         //same as getMetafile with an exception for charts
199         //for charts a metafile with a higher resolution is created, because charts have resolution dependent content
200         bool local_getMetaFile_WithSpecialChartHandling( const uno::Reference< lang::XComponent >& 	  xSource,
201                   const uno::Reference< drawing::XDrawPage >&     xContainingPage,
202                   GDIMetaFile&                                    rMtf,
203                   int                                             mtfLoadFlags,
204                   const uno::Reference< uno::XComponentContext >& rxContext )
205         {
206             uno::Reference<beans::XPropertySet> xProp( xSource, uno::UNO_QUERY );
207             rtl::OUString sCLSID;
208             getPropertyValue( sCLSID, xProp, OUSTR("CLSID"));
209             if( sCLSID.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("12DCAE26-281F-416F-a234-c3086127382e")) && local_getMetafileForChart( xSource, xContainingPage, rMtf ) )
210                 return true;
211             return getMetaFile( xSource, xContainingPage, rMtf, mtfLoadFlags, rxContext );
212         }
213 
214 
215         //////////////////////////////////////////////////////////////////////
216         //
217         // Private methods
218         //
219         //////////////////////////////////////////////////////////////////////
220 
221         GDIMetaFileSharedPtr DrawShape::forceScrollTextMetaFile()
222         {
223             if ((mnCurrMtfLoadFlags & MTF_LOAD_SCROLL_TEXT_MTF) != MTF_LOAD_SCROLL_TEXT_MTF)
224             {
225                 // reload with added flags:
226                 mpCurrMtf.reset( new GDIMetaFile );
227                 mnCurrMtfLoadFlags |= MTF_LOAD_SCROLL_TEXT_MTF;
228                 local_getMetaFile_WithSpecialChartHandling(
229                     uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY),
230                     mxPage, *mpCurrMtf, mnCurrMtfLoadFlags,
231                     mxComponentContext );
232 
233                 // TODO(F1): Currently, the scroll metafile will
234                 // never contain any verbose text comments. Thus,
235                 // can only display the full mtf content, no
236                 // subsets.
237                 maSubsetting.reset( mpCurrMtf );
238 
239                 // adapt maBounds. the requested scroll text metafile
240                 // will typically have dimension different from the
241                 // actual shape
242                 ::basegfx::B2DRectangle aScrollRect, aPaintRect;
243                 ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect,
244                                                               aPaintRect,
245                                                               mpCurrMtf ),
246                                   "DrawShape::forceScrollTextMetaFile(): Could "
247                                   "not extract scroll anim rectangles from mtf" );
248 
249                 // take the larger one of the two rectangles (that
250                 // should be the bound rect of the retrieved
251                 // metafile)
252                 if( aScrollRect.isInside( aPaintRect ) )
253                     maBounds = aScrollRect;
254                 else
255                     maBounds = aPaintRect;
256             }
257             return mpCurrMtf;
258         }
259 
260         void DrawShape::updateStateIds() const
261         {
262             // Update the states, we've just redrawn or created a new
263             // attribute layer.
264             if( mpAttributeLayer )
265             {
266                 mnAttributeTransformationState = mpAttributeLayer->getTransformationState();
267                 mnAttributeClipState = mpAttributeLayer->getClipState();
268                 mnAttributeAlphaState = mpAttributeLayer->getAlphaState();
269                 mnAttributePositionState = mpAttributeLayer->getPositionState();
270                 mnAttributeContentState = mpAttributeLayer->getContentState();
271                 mnAttributeVisibilityState = mpAttributeLayer->getVisibilityState();
272             }
273         }
274 
275         void DrawShape::ensureVerboseMtfComments() const
276         {
277             // TODO(F1): Text effects don't currently work for drawing
278             // layer animations.
279 
280             // only touch mpCurrMtf, if we're not a DrawingLayer
281             // animation.
282             if( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) == 0 &&
283                 maAnimationFrames.empty() )
284             {
285                 ENSURE_OR_THROW( !maSubsetting.hasSubsetShapes(),
286                                   "DrawShape::ensureVerboseMtfComments(): reloading the metafile "
287                                   "with active child subsets will wreak havoc on the view!" );
288                 ENSURE_OR_THROW( maSubsetting.getSubsetNode().isEmpty(),
289                                   "DrawShape::ensureVerboseMtfComments(): reloading the metafile "
290                                   "for an ALREADY SUBSETTED shape is not possible!" );
291 
292                 // re-fetch metafile with comments
293                 // note that, in case of shapes without text, the new
294                 // metafile might still not provide any useful
295                 // subsetting information!
296                 mpCurrMtf.reset( new GDIMetaFile );
297                 mnCurrMtfLoadFlags |= MTF_LOAD_VERBOSE_COMMENTS;
298                 local_getMetaFile_WithSpecialChartHandling(
299                     uno::Reference<lang::XComponent>(mxShape, uno::UNO_QUERY),
300                     mxPage, *mpCurrMtf, mnCurrMtfLoadFlags,
301                     mxComponentContext );
302 
303                 maSubsetting.reset( maSubsetting.getSubsetNode(),
304                                     mpCurrMtf );
305             }
306         }
307 
308         ViewShape::RenderArgs DrawShape::getViewRenderArgs() const
309         {
310             return ViewShape::RenderArgs(
311                 maBounds,
312                 getUpdateArea(),
313                 getBounds(),
314                 getActualUnitShapeBounds(),
315                 mpAttributeLayer,
316                 maSubsetting.getActiveSubsets(),
317                 mnPriority);
318         }
319 
320         bool DrawShape::implRender( int nUpdateFlags ) const
321         {
322             RTL_LOGFILE_CONTEXT( aLog, "::presentation::internal::DrawShape::implRender()" );
323             RTL_LOGFILE_CONTEXT_TRACE1( aLog, "::presentation::internal::DrawShape: 0x%X", this );
324 
325             // will perform the update now, clear update-enforcing
326             // flags
327             mbForceUpdate = false;
328             mbAttributeLayerRevoked = false;
329 
330             ENSURE_OR_RETURN_FALSE( !maViewShapes.empty(),
331                                "DrawShape::implRender(): render called on DrawShape without views" );
332 
333             if( maBounds.isEmpty() )
334             {
335                 // zero-sized shapes are effectively invisible,
336                 // thus, we save us the rendering...
337                 return true;
338             }
339 
340             // redraw all view shapes, by calling their update() method
341             if( ::std::count_if( maViewShapes.begin(),
342                                  maViewShapes.end(),
343                                  ::boost::bind<bool>(
344                                      ::boost::mem_fn( &ViewShape::update ), // though _theoretically_,
345                                      										// bind should eat this even
346                                      										// with _1 being a shared_ptr,
347                                      										// it does _not_ for MSVC without
348                                      										// the extra mem_fn. WTF.
349                                      _1,
350                                      ::boost::cref( mpCurrMtf ),
351                                      ::boost::cref(
352                                          getViewRenderArgs() ),
353                                      nUpdateFlags,
354                                      isVisible() ) )
355                 != static_cast<ViewShapeVector::difference_type>(maViewShapes.size()) )
356             {
357                 // at least one of the ViewShape::update() calls did return
358                 // false - update failed on at least one ViewLayer
359                 return false;
360             }
361 
362             // successfully redrawn - update state IDs to detect next changes
363             updateStateIds();
364 
365             return true;
366         }
367 
368         int DrawShape::getUpdateFlags() const
369         {
370             // default: update nothing, unless ShapeAttributeStack
371             // tells us below, or if the attribute layer was revoked
372             int nUpdateFlags(ViewShape::NONE);
373 
374             // possibly the whole shape content changed
375             if( mbAttributeLayerRevoked )
376                 nUpdateFlags = ViewShape::CONTENT;
377 
378 
379             // determine what has to be updated
380             // --------------------------------
381 
382             // do we have an attribute layer?
383             if( mpAttributeLayer )
384             {
385                 // Prevent nUpdateFlags to be modified when the shape is not
386                 // visible, except when it just was hidden.
387                 if (mpAttributeLayer->getVisibility()
388                     || mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
389                 {
390                     if (mpAttributeLayer->getVisibilityState() != mnAttributeVisibilityState )
391                     {
392                         // Change of the visibility state is mapped to
393                         // content change because when the visibility
394                         // changes then usually a sprite is shown or hidden
395                         // and the background under has to be painted once.
396                         nUpdateFlags |= ViewShape::CONTENT;
397                     }
398 
399                     // TODO(P1): This can be done without conditional branching.
400                     // See HAKMEM.
401                     if( mpAttributeLayer->getPositionState() != mnAttributePositionState )
402                     {
403                         nUpdateFlags |= ViewShape::POSITION;
404                     }
405                     if( mpAttributeLayer->getAlphaState() != mnAttributeAlphaState )
406                     {
407                         nUpdateFlags |= ViewShape::ALPHA;
408                     }
409                     if( mpAttributeLayer->getClipState() != mnAttributeClipState )
410                     {
411                         nUpdateFlags |= ViewShape::CLIP;
412                     }
413                     if( mpAttributeLayer->getTransformationState() != mnAttributeTransformationState )
414                     {
415                         nUpdateFlags |= ViewShape::TRANSFORMATION;
416                     }
417                     if( mpAttributeLayer->getContentState() != mnAttributeContentState )
418                     {
419                         nUpdateFlags |= ViewShape::CONTENT;
420                     }
421                 }
422             }
423 
424             return nUpdateFlags;
425         }
426 
427         ::basegfx::B2DRectangle DrawShape::getActualUnitShapeBounds() const
428         {
429             ENSURE_OR_THROW( !maViewShapes.empty(),
430                               "DrawShape::getActualUnitShapeBounds(): called on DrawShape without views" );
431 
432             const VectorOfDocTreeNodes& rSubsets(
433                 maSubsetting.getActiveSubsets() );
434 
435             const ::basegfx::B2DRectangle aDefaultBounds( 0.0,0.0,1.0,1.0 );
436 
437             // perform the cheapest check first
438             if( rSubsets.empty() )
439             {
440                 // if subset contains the whole shape, no need to call
441                 // the somewhat expensive bound calculation, since as
442                 // long as the subset is empty, this branch will be
443                 // taken.
444                 return aDefaultBounds;
445             }
446             else
447             {
448                 OSL_ENSURE( rSubsets.size() != 1 ||
449                             !rSubsets.front().isEmpty(),
450                             "DrawShape::getActualUnitShapeBounds() expects a "
451                             "_non-empty_ subset vector for a subsetted shape!" );
452 
453                 // are the cached bounds still valid?
454                 if( !maCurrentShapeUnitBounds )
455                 {
456                     // no, (re)generate them
457                     // =====================
458 
459                     // setup cached values to defaults (might fail to
460                     // retrieve true bounds below)
461                     maCurrentShapeUnitBounds.reset( aDefaultBounds );
462 
463                     // TODO(P2): the subset of the master shape (that from
464                     // which the subsets are subtracted) changes
465                     // relatively often (every time a subset shape is
466                     // added or removed). Maybe we should exclude it here,
467                     // always assuming full bounds?
468 
469                     ::cppcanvas::CanvasSharedPtr pDestinationCanvas(
470                         maViewShapes.front()->getViewLayer()->getCanvas() );
471 
472                     // TODO(Q2): Although this _is_ currently
473                     // view-agnostic, it might not stay like
474                     // that. Maybe this method should again be moved
475                     // to the ViewShape
476                     ::cppcanvas::RendererSharedPtr pRenderer(
477                         maViewShapes.front()->getRenderer(
478                             pDestinationCanvas, mpCurrMtf, mpAttributeLayer ) );
479 
480                     // If we cannot not prefetch, be defensive and assume
481                     // full shape size
482                     if( pRenderer )
483                     {
484                         // temporarily, switch total transformation to identity
485                         // (need the bounds in the [0,1]x[0,1] unit coordinate
486                         // system.
487                         ::basegfx::B2DHomMatrix 	 aEmptyTransformation;
488 
489                         ::basegfx::B2DHomMatrix 	 aOldTransform( pDestinationCanvas->getTransformation() );
490                         pDestinationCanvas->setTransformation( aEmptyTransformation );
491                         pRenderer->setTransformation( aEmptyTransformation );
492 
493                         // restore old transformation when leaving the scope
494                         const ::comphelper::ScopeGuard aGuard(
495                             boost::bind( &::cppcanvas::Canvas::setTransformation,
496                                          pDestinationCanvas, aOldTransform ) );
497 
498 
499                         // retrieve bounds for subset of whole metafile
500                         // --------------------------------------------
501 
502                         ::basegfx::B2DRange aTotalBounds;
503 
504                         // cannot use ::boost::bind, ::basegfx::B2DRange::expand()
505                         // is overloaded.
506                         VectorOfDocTreeNodes::const_iterator 		aCurr( rSubsets.begin() );
507                         const VectorOfDocTreeNodes::const_iterator	aEnd( rSubsets.end() );
508                         while( aCurr != aEnd )
509                         {
510                             aTotalBounds.expand( pRenderer->getSubsetArea(
511                                                      aCurr->getStartIndex(),
512                                                      aCurr->getEndIndex() )  );
513                             ++aCurr;
514                         }
515 
516                         OSL_ENSURE( aTotalBounds.getMinX() >= -0.1 &&
517                                     aTotalBounds.getMinY() >= -0.1 &&
518                                     aTotalBounds.getMaxX() <= 1.1 &&
519                                     aTotalBounds.getMaxY() <= 1.1,
520                                     "DrawShape::getActualUnitShapeBounds(): bounds noticeably larger than original shape - clipping!" );
521 
522                         // really make sure no shape appears larger than its
523                         // original bounds (there _are_ some pathologic cases,
524                         // especially when imported from PPT, that have
525                         // e.g. obscenely large polygon bounds)
526                         aTotalBounds.intersect(
527                             ::basegfx::B2DRange( 0.0, 0.0,
528                                                  1.0, 1.0 ));
529 
530                         maCurrentShapeUnitBounds.reset( aTotalBounds );
531                     }
532                 }
533 
534                 return *maCurrentShapeUnitBounds;
535             }
536         }
537 
538         DrawShape::DrawShape( const uno::Reference< drawing::XShape >& 		xShape,
539                               const uno::Reference< drawing::XDrawPage >&	xContainingPage,
540                               double										nPrio,
541                               bool											bForeignSource,
542                               const SlideShowContext&                       rContext ) :
543             mxShape( xShape ),
544             mxPage( xContainingPage ),
545             maAnimationFrames(), // empty, we don't have no intrinsic animation
546             mnCurrFrame(0),
547             mpCurrMtf(),
548             mnCurrMtfLoadFlags( bForeignSource
549                                 ? MTF_LOAD_FOREIGN_SOURCE : MTF_LOAD_NONE ),
550             maCurrentShapeUnitBounds(),
551             mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
552             maBounds( getAPIShapeBounds( xShape ) ),
553             mpAttributeLayer(),
554             mpIntrinsicAnimationActivity(),
555             mnAttributeTransformationState(0),
556             mnAttributeClipState(0),
557             mnAttributeAlphaState(0),
558             mnAttributePositionState(0),
559             mnAttributeContentState(0),
560             mnAttributeVisibilityState(0),
561             maViewShapes(),
562             mxComponentContext( rContext.mxComponentContext ),
563             maHyperlinkIndices(),
564             maHyperlinkRegions(),
565             maSubsetting(),
566             mnIsAnimatedCount(0),
567             mnAnimationLoopCount(0),
568             meCycleMode(CYCLE_LOOP),
569             mbIsVisible( true ),
570             mbForceUpdate( false ),
571             mbAttributeLayerRevoked( false ),
572             mbDrawingLayerAnim( false )
573         {
574             ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
575             ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
576 
577             // check for drawing layer animations:
578             drawing::TextAnimationKind eKind = drawing::TextAnimationKind_NONE;
579             uno::Reference<beans::XPropertySet> xPropSet( mxShape,
580                                                           uno::UNO_QUERY );
581             if( xPropSet.is() )
582                 getPropertyValue( eKind, xPropSet,
583                                   OUSTR("TextAnimationKind") );
584             mbDrawingLayerAnim = (eKind != drawing::TextAnimationKind_NONE);
585 
586             // must NOT be called from within initializer list, uses
587             // state from mnCurrMtfLoadFlags!
588             mpCurrMtf.reset( new GDIMetaFile );
589             local_getMetaFile_WithSpecialChartHandling(
590                 uno::Reference<lang::XComponent>(xShape, uno::UNO_QUERY),
591                 xContainingPage, *mpCurrMtf, mnCurrMtfLoadFlags,
592                 mxComponentContext );
593             ENSURE_OR_THROW( mpCurrMtf,
594                               "DrawShape::DrawShape(): Invalid metafile" );
595             maSubsetting.reset( mpCurrMtf );
596 
597             prepareHyperlinkIndices();
598         }
599 
600         DrawShape::DrawShape( const uno::Reference< drawing::XShape >& 		xShape,
601                               const uno::Reference< drawing::XDrawPage >&	xContainingPage,
602                               double										nPrio,
603                               const Graphic&								rGraphic,
604                               const SlideShowContext&                       rContext ) :
605             mxShape( xShape ),
606             mxPage( xContainingPage ),
607             maAnimationFrames(),
608             mnCurrFrame(0),
609             mpCurrMtf(),
610             mnCurrMtfLoadFlags( MTF_LOAD_NONE ),
611             maCurrentShapeUnitBounds(),
612             mnPriority( nPrio ), // TODO(F1): When ZOrder someday becomes usable: make this ( getAPIShapePrio( xShape ) ),
613             maBounds( getAPIShapeBounds( xShape ) ),
614             mpAttributeLayer(),
615             mpIntrinsicAnimationActivity(),
616             mnAttributeTransformationState(0),
617             mnAttributeClipState(0),
618             mnAttributeAlphaState(0),
619             mnAttributePositionState(0),
620             mnAttributeContentState(0),
621             mnAttributeVisibilityState(0),
622             maViewShapes(),
623             mxComponentContext( rContext.mxComponentContext ),
624             maHyperlinkIndices(),
625             maHyperlinkRegions(),
626             maSubsetting(),
627             mnIsAnimatedCount(0),
628             mnAnimationLoopCount(0),
629             meCycleMode(CYCLE_LOOP),
630             mbIsVisible( true ),
631             mbForceUpdate( false ),
632             mbAttributeLayerRevoked( false ),
633             mbDrawingLayerAnim( false )
634         {
635             ENSURE_OR_THROW( rGraphic.IsAnimated(),
636                               "DrawShape::DrawShape(): Graphic is no animation" );
637 
638             getAnimationFromGraphic( maAnimationFrames,
639                                      mnAnimationLoopCount,
640                                      meCycleMode,
641                                      rGraphic );
642 
643             ENSURE_OR_THROW( !maAnimationFrames.empty() &&
644                               maAnimationFrames.front().mpMtf,
645                               "DrawShape::DrawShape(): " );
646             mpCurrMtf = maAnimationFrames.front().mpMtf;
647 
648             ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
649             ENSURE_OR_THROW( mxPage.is(), "DrawShape::DrawShape(): Invalid containing page" );
650             ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
651         }
652 
653         DrawShape::DrawShape( const DrawShape& 		rSrc,
654                               const DocTreeNode& 	rTreeNode,
655                               double				nPrio ) :
656             mxShape( rSrc.mxShape ),
657             mxPage( rSrc.mxPage ),
658             maAnimationFrames(), // don't copy animations for subsets,
659                                  // only the current frame!
660             mnCurrFrame(0),
661             mpCurrMtf( rSrc.mpCurrMtf ),
662             mnCurrMtfLoadFlags( rSrc.mnCurrMtfLoadFlags ),
663             maCurrentShapeUnitBounds(),
664             mnPriority( nPrio ),
665             maBounds( rSrc.maBounds ),
666             mpAttributeLayer(),
667             mpIntrinsicAnimationActivity(),
668             mnAttributeTransformationState(0),
669             mnAttributeClipState(0),
670             mnAttributeAlphaState(0),
671             mnAttributePositionState(0),
672             mnAttributeContentState(0),
673             mnAttributeVisibilityState(0),
674             maViewShapes(),
675             mxComponentContext( rSrc.mxComponentContext ),
676             maHyperlinkIndices(),
677             maHyperlinkRegions(),
678             maSubsetting( rTreeNode, mpCurrMtf ),
679             mnIsAnimatedCount(0),
680             mnAnimationLoopCount(0),
681             meCycleMode(CYCLE_LOOP),
682             mbIsVisible( rSrc.mbIsVisible ),
683             mbForceUpdate( false ),
684             mbAttributeLayerRevoked( false ),
685             mbDrawingLayerAnim( false )
686         {
687             ENSURE_OR_THROW( mxShape.is(), "DrawShape::DrawShape(): Invalid XShape" );
688             ENSURE_OR_THROW( mpCurrMtf, "DrawShape::DrawShape(): Invalid metafile" );
689 
690             // xxx todo: currently not implemented for subsetted shapes;
691             //           would mean modifying set of hyperlink regions when
692             //           subsetting text portions. N.B.: there's already an
693             //           issue for this #i72828#
694         }
695 
696         //////////////////////////////////////////////////////////////////////
697         //
698         // Public methods
699         //
700         //////////////////////////////////////////////////////////////////////
701 
702         DrawShapeSharedPtr DrawShape::create(
703             const uno::Reference< drawing::XShape >& 	xShape,
704             const uno::Reference< drawing::XDrawPage >&	xContainingPage,
705             double										nPrio,
706             bool										bForeignSource,
707             const SlideShowContext&                     rContext )
708         {
709             DrawShapeSharedPtr pShape( new DrawShape(xShape,
710                                                      xContainingPage,
711                                                      nPrio,
712                                                      bForeignSource,
713                                                      rContext) );
714 
715             if( pShape->hasIntrinsicAnimation() )
716             {
717                 OSL_ASSERT( pShape->maAnimationFrames.empty() );
718                 if( pShape->getNumberOfTreeNodes(
719                         DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH) > 0 )
720                 {
721                     pShape->mpIntrinsicAnimationActivity =
722                         createDrawingLayerAnimActivity(
723                             rContext,
724                             pShape);
725                 }
726             }
727 
728             if( pShape->hasHyperlinks() )
729                 rContext.mpSubsettableShapeManager->addHyperlinkArea( pShape );
730 
731             return pShape;
732         }
733 
734         DrawShapeSharedPtr DrawShape::create(
735             const uno::Reference< drawing::XShape >& 	xShape,
736             const uno::Reference< drawing::XDrawPage >&	xContainingPage,
737             double										nPrio,
738             const Graphic&								rGraphic,
739             const SlideShowContext&                     rContext )
740         {
741             DrawShapeSharedPtr pShape( new DrawShape(xShape,
742                                                      xContainingPage,
743                                                      nPrio,
744                                                      rGraphic,
745                                                      rContext) );
746 
747             if( pShape->hasIntrinsicAnimation() )
748             {
749                 OSL_ASSERT( !pShape->maAnimationFrames.empty() );
750 
751                 std::vector<double> aTimeout;
752                 std::transform(
753                     pShape->maAnimationFrames.begin(),
754                     pShape->maAnimationFrames.end(),
755                     std::back_insert_iterator< std::vector<double> >( aTimeout ),
756                     boost::mem_fn(&MtfAnimationFrame::getDuration) );
757 
758                 WakeupEventSharedPtr pWakeupEvent(
759                     new WakeupEvent( rContext.mrEventQueue.getTimer(),
760                                      rContext.mrActivitiesQueue ) );
761 
762                 ActivitySharedPtr pActivity =
763                     createIntrinsicAnimationActivity(
764                         rContext,
765                         pShape,
766                         pWakeupEvent,
767                         aTimeout,
768                         pShape->mnAnimationLoopCount,
769                         pShape->meCycleMode);
770 
771                 pWakeupEvent->setActivity( pActivity );
772                 pShape->mpIntrinsicAnimationActivity = pActivity;
773             }
774 
775             OSL_ENSURE( !pShape->hasHyperlinks(),
776                         "DrawShape::create(): graphic-only shapes must not have hyperlinks!" );
777 
778             return pShape;
779         }
780 
781         DrawShape::~DrawShape()
782         {
783             try
784             {
785                 // dispose intrinsic animation activity, else, it will
786                 // linger forever
787                 ActivitySharedPtr pActivity( mpIntrinsicAnimationActivity.lock() );
788                 if( pActivity )
789                     pActivity->dispose();
790             }
791             catch (uno::Exception &)
792             {
793                 OSL_ENSURE( false, rtl::OUStringToOString(
794                                 comphelper::anyToString(
795                                     cppu::getCaughtException() ),
796                                 RTL_TEXTENCODING_UTF8 ).getStr() );
797             }
798         }
799 
800         uno::Reference< drawing::XShape > DrawShape::getXShape() const
801         {
802             return mxShape;
803         }
804 
805         void DrawShape::addViewLayer( const ViewLayerSharedPtr& rNewLayer,
806                                       bool						bRedrawLayer )
807         {
808             ViewShapeVector::iterator aEnd( maViewShapes.end() );
809 
810             // already added?
811             if( ::std::find_if( maViewShapes.begin(),
812                                 aEnd,
813                                 ::boost::bind<bool>(
814                                     ::std::equal_to< ViewLayerSharedPtr >(),
815                                     ::boost::bind( &ViewShape::getViewLayer,
816                                                    _1 ),
817                                     ::boost::cref( rNewLayer ) ) ) != aEnd )
818             {
819                 // yes, nothing to do
820                 return;
821             }
822 
823             ViewShapeSharedPtr pNewShape( new ViewShape( rNewLayer ) );
824 
825             maViewShapes.push_back( pNewShape );
826 
827             // pass on animation state
828             if( mnIsAnimatedCount )
829             {
830                 for( int i=0; i<mnIsAnimatedCount; ++i )
831                     pNewShape->enterAnimationMode();
832             }
833 
834             // render the Shape on the newly added ViewLayer
835             if( bRedrawLayer )
836             {
837                 pNewShape->update( mpCurrMtf,
838                                    getViewRenderArgs(),
839                                    ViewShape::FORCE,
840                                    isVisible() );
841             }
842         }
843 
844         bool DrawShape::removeViewLayer( const ViewLayerSharedPtr& rLayer )
845         {
846             const ViewShapeVector::iterator aEnd( maViewShapes.end() );
847 
848             OSL_ENSURE( ::std::count_if(maViewShapes.begin(),
849                                         aEnd,
850                                         ::boost::bind<bool>(
851                                             ::std::equal_to< ViewLayerSharedPtr >(),
852                                             ::boost::bind( &ViewShape::getViewLayer,
853                                                            _1 ),
854                                             ::boost::cref( rLayer ) ) ) < 2,
855                         "DrawShape::removeViewLayer(): Duplicate ViewLayer entries!" );
856 
857             ViewShapeVector::iterator aIter;
858 
859             if( (aIter=::std::remove_if( maViewShapes.begin(),
860                                          aEnd,
861                                          ::boost::bind<bool>(
862                                              ::std::equal_to< ViewLayerSharedPtr >(),
863                                              ::boost::bind( &ViewShape::getViewLayer,
864                                                             _1 ),
865                                              ::boost::cref( rLayer ) ) )) == aEnd )
866             {
867                 // view layer seemingly was not added, failed
868                 return false;
869             }
870 
871             // actually erase from container
872             maViewShapes.erase( aIter, aEnd );
873 
874             return true;
875         }
876 
877         bool DrawShape::clearAllViewLayers()
878         {
879             maViewShapes.clear();
880             return true;
881         }
882 
883         bool DrawShape::update() const
884         {
885             if( mbForceUpdate )
886             {
887                 return render();
888             }
889             else
890             {
891                 return implRender( getUpdateFlags() );
892             }
893         }
894 
895         bool DrawShape::render() const
896         {
897             // force redraw. Have to also pass on the update flags,
898             // because e.g. content update (regeneration of the
899             // metafile renderer) is normally not performed. A simple
900             // ViewShape::FORCE would only paint the metafile in its
901             // old state.
902             return implRender( ViewShape::FORCE | getUpdateFlags() );
903         }
904 
905         bool DrawShape::isContentChanged() const
906         {
907             return mbForceUpdate ?
908                 true :
909                 getUpdateFlags() != ViewShape::NONE;
910         }
911 
912 
913         ::basegfx::B2DRectangle DrawShape::getBounds() const
914         {
915             // little optimization: for non-modified shapes, we don't
916             // create an ShapeAttributeStack, and therefore also don't
917             // have to check it.
918             return getShapePosSize( maBounds,
919                                     mpAttributeLayer );
920         }
921 
922         ::basegfx::B2DRectangle DrawShape::getDomBounds() const
923         {
924             return maBounds;
925         }
926 
927         namespace
928         {
929             /** Functor expanding AA border for each passed ViewShape
930 
931             	Could not use ::boost::bind here, since
932             	B2DRange::expand is overloaded (which yields one or
933             	the other template type deduction ambiguous)
934              */
935             class Expander
936             {
937             public:
938                 Expander( ::basegfx::B2DSize& rBounds ) :
939                     mrBounds( rBounds )
940                 {
941                 }
942 
943                 void operator()( const ViewShapeSharedPtr& rShape ) const
944                 {
945                     const ::basegfx::B2DSize& rShapeBorder( rShape->getAntialiasingBorder() );
946 
947                     mrBounds.setX(
948                         ::std::max(
949                             rShapeBorder.getX(),
950                             mrBounds.getX() ) );
951                     mrBounds.setY(
952                         ::std::max(
953                             rShapeBorder.getY(),
954                             mrBounds.getY() ) );
955                 }
956 
957             private:
958                 ::basegfx::B2DSize& mrBounds;
959             };
960         }
961 
962         ::basegfx::B2DRectangle DrawShape::getUpdateArea() const
963         {
964             ::basegfx::B2DRectangle aBounds;
965 
966             // an already empty shape bound need no further
967             // treatment. In fact, any changes applied below would
968             // actually remove the special empty state, thus, don't
969             // change!
970             if( !maBounds.isEmpty() )
971             {
972                 basegfx::B2DRectangle aUnitBounds(0.0,0.0,1.0,1.0);
973 
974                 if( !maViewShapes.empty() )
975                     aUnitBounds = getActualUnitShapeBounds();
976 
977                 if( !aUnitBounds.isEmpty() )
978                 {
979                     if( mpAttributeLayer )
980                     {
981                         // calc actual shape area (in user coordinate
982                         // space) from the transformation as given by the
983                         // shape attribute layer
984                         aBounds = getShapeUpdateArea( aUnitBounds,
985                                                       getShapeTransformation( getBounds(),
986                                                                               mpAttributeLayer ),
987                                                       mpAttributeLayer );
988                     }
989                     else
990                     {
991                         // no attribute layer, thus, the true shape bounds
992                         // can be directly derived from the XShape bound
993                         // attribute
994                         aBounds = getShapeUpdateArea( aUnitBounds,
995                                                       maBounds );
996                     }
997 
998                     if( !maViewShapes.empty() )
999                     {
1000                         // determine border needed for antialiasing the shape
1001                         ::basegfx::B2DSize aAABorder(0.0,0.0);
1002 
1003                         // for every view, get AA border and 'expand' aAABorder
1004                         // appropriately.
1005                         ::std::for_each( maViewShapes.begin(),
1006                                          maViewShapes.end(),
1007                                          Expander( aAABorder ) );
1008 
1009                         // add calculated AA border to aBounds
1010                         aBounds = ::basegfx::B2DRectangle( aBounds.getMinX() - aAABorder.getX(),
1011                                                            aBounds.getMinY() - aAABorder.getY(),
1012                                                            aBounds.getMaxX() + aAABorder.getX(),
1013                                                            aBounds.getMaxY() + aAABorder.getY() );
1014                     }
1015                 }
1016             }
1017 
1018             return aBounds;
1019         }
1020 
1021         bool DrawShape::isVisible() const
1022         {
1023             bool bIsVisible( mbIsVisible );
1024 
1025             if( mpAttributeLayer )
1026             {
1027                 // check whether visibility and alpha are not default
1028                 // (mpAttributeLayer->isVisibilityValid() returns true
1029                 // then): bVisible becomes true, if shape visibility
1030                 // is on and alpha is not 0.0 (fully transparent)
1031                 if( mpAttributeLayer->isVisibilityValid() )
1032                     bIsVisible = mpAttributeLayer->getVisibility();
1033 
1034                 // only touch bIsVisible, if the shape is still
1035                 // visible - if getVisibility already made us
1036                 // invisible, no alpha value will make us appear
1037                 // again.
1038                 if( bIsVisible && mpAttributeLayer->isAlphaValid() )
1039                     bIsVisible = !::basegfx::fTools::equalZero( mpAttributeLayer->getAlpha() );
1040             }
1041 
1042             return bIsVisible;
1043         }
1044 
1045         double DrawShape::getPriority() const
1046         {
1047             return mnPriority;
1048         }
1049 
1050         bool DrawShape::isBackgroundDetached() const
1051         {
1052             return mnIsAnimatedCount > 0;
1053         }
1054 
1055         bool DrawShape::hasIntrinsicAnimation() const
1056         {
1057             return (!maAnimationFrames.empty() || mbDrawingLayerAnim);
1058         }
1059 
1060         bool DrawShape::setIntrinsicAnimationFrame( ::std::size_t nCurrFrame )
1061         {
1062             ENSURE_OR_RETURN_FALSE( nCurrFrame < maAnimationFrames.size(),
1063                                "DrawShape::setIntrinsicAnimationFrame(): frame index out of bounds" );
1064 
1065             if( mnCurrFrame != nCurrFrame )
1066             {
1067                 mnCurrFrame   = nCurrFrame;
1068                 mpCurrMtf     = maAnimationFrames[ mnCurrFrame ].mpMtf;
1069                 mbForceUpdate = true;
1070             }
1071 
1072             return true;
1073         }
1074 
1075         // hyperlink support
1076         void DrawShape::prepareHyperlinkIndices() const
1077         {
1078             if ( !maHyperlinkIndices.empty())
1079             {
1080                 maHyperlinkIndices.clear();
1081                 maHyperlinkRegions.clear();
1082             }
1083 
1084             sal_Int32 nIndex = 0;
1085             for ( MetaAction * pCurrAct = mpCurrMtf->FirstAction();
1086                   pCurrAct != 0; pCurrAct = mpCurrMtf->NextAction() )
1087             {
1088                 if (pCurrAct->GetType() == META_COMMENT_ACTION) {
1089                     MetaCommentAction * pAct =
1090                         static_cast<MetaCommentAction *>(pCurrAct);
1091                     // skip comment if not a special XTEXT comment
1092                     if (pAct->GetComment().CompareIgnoreCaseToAscii(
1093                             RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN") ) ==
1094                         COMPARE_EQUAL &&
1095                         // e.g. date field doesn't have data!
1096                         // currently assuming that only url field, this is
1097                         // somehow fragile! xxx todo if possible
1098                         pAct->GetData() != 0 &&
1099                         pAct->GetDataSize() > 0)
1100                     {
1101                         if (!maHyperlinkIndices.empty() &&
1102                             maHyperlinkIndices.back().second == -1) {
1103                             OSL_ENSURE( false, "### pending FIELD_SEQ_END!" );
1104                             maHyperlinkIndices.pop_back();
1105                             maHyperlinkRegions.pop_back();
1106                         }
1107                         maHyperlinkIndices.push_back(
1108                             HyperlinkIndexPair( nIndex + 1,
1109                                                 -1 /* to be filled below */ ) );
1110                         maHyperlinkRegions.push_back(
1111                             HyperlinkRegion(
1112                                 basegfx::B2DRectangle(),
1113                                 rtl::OUString(
1114                                     reinterpret_cast<sal_Unicode const*>(
1115                                         pAct->GetData()),
1116                                     pAct->GetDataSize() / sizeof(sal_Unicode) )
1117                                 ) );
1118                     }
1119                     else if (pAct->GetComment().CompareIgnoreCaseToAscii(
1120                                  RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_END")) ==
1121                              COMPARE_EQUAL &&
1122                              // pending end is expected:
1123                              !maHyperlinkIndices.empty() &&
1124                              maHyperlinkIndices.back().second == -1)
1125                     {
1126                         maHyperlinkIndices.back().second = nIndex;
1127                     }
1128                     ++nIndex;
1129                 }
1130                 else
1131                     nIndex += getNextActionOffset(pCurrAct);
1132             }
1133             if (!maHyperlinkIndices.empty() &&
1134                 maHyperlinkIndices.back().second == -1) {
1135                 OSL_ENSURE( false, "### pending FIELD_SEQ_END!" );
1136                 maHyperlinkIndices.pop_back();
1137                 maHyperlinkRegions.pop_back();
1138             }
1139             OSL_ASSERT( maHyperlinkIndices.size() == maHyperlinkRegions.size());
1140         }
1141 
1142         bool DrawShape::hasHyperlinks() const
1143         {
1144             return ! maHyperlinkRegions.empty();
1145         }
1146 
1147         HyperlinkArea::HyperlinkRegions DrawShape::getHyperlinkRegions() const
1148         {
1149             OSL_ASSERT( !maViewShapes.empty() );
1150 
1151             if( !isVisible() )
1152                 return HyperlinkArea::HyperlinkRegions();
1153 
1154             // late init, determine regions:
1155             if( !maHyperlinkRegions.empty() &&
1156                 !maViewShapes.empty() &&
1157                 // region already inited?
1158                 maHyperlinkRegions.front().first.getWidth() == 0 &&
1159                 maHyperlinkRegions.front().first.getHeight() == 0 &&
1160                 maHyperlinkRegions.size() == maHyperlinkIndices.size() )
1161             {
1162                 // TODO(Q2): Although this _is_ currently
1163                 // view-agnostic, it might not stay like that.
1164                 ViewShapeSharedPtr const& pViewShape = maViewShapes.front();
1165                 cppcanvas::CanvasSharedPtr const pCanvas(
1166                     pViewShape->getViewLayer()->getCanvas() );
1167 
1168                 // reuse Renderer of first view shape:
1169                 cppcanvas::RendererSharedPtr const pRenderer(
1170                     pViewShape->getRenderer(
1171                         pCanvas, mpCurrMtf, mpAttributeLayer ) );
1172 
1173                 OSL_ASSERT( pRenderer );
1174 
1175                 if (pRenderer)
1176                 {
1177                     basegfx::B2DHomMatrix const aOldTransform(
1178                         pCanvas->getTransformation() );
1179                     basegfx::B2DHomMatrix aTransform;
1180                     pCanvas->setTransformation( aTransform /* empty */ );
1181 
1182                     comphelper::ScopeGuard const resetOldTransformation(
1183                         boost::bind( &cppcanvas::Canvas::setTransformation,
1184                                      pCanvas.get(),
1185                                      boost::cref(aOldTransform) ));
1186 
1187                     aTransform.scale( maBounds.getWidth(),
1188                                       maBounds.getHeight() );
1189                     pRenderer->setTransformation( aTransform );
1190                     pRenderer->setClip();
1191 
1192                     for( std::size_t pos = maHyperlinkRegions.size(); pos--; )
1193                     {
1194                         // get region:
1195                         HyperlinkIndexPair const& rIndices = maHyperlinkIndices[pos];
1196                         basegfx::B2DRectangle const region(
1197                             pRenderer->getSubsetArea( rIndices.first,
1198                                                       rIndices.second ));
1199                         maHyperlinkRegions[pos].first = region;
1200                     }
1201                 }
1202             }
1203 
1204             // shift shape-relative hyperlink regions to
1205             // slide-absolute position
1206 
1207             HyperlinkRegions aTranslatedRegions;
1208             const basegfx::B2DPoint& rOffset(getBounds().getMinimum());
1209             HyperlinkRegions::const_iterator       aIter( maHyperlinkRegions.begin() );
1210             HyperlinkRegions::const_iterator const aEnd ( maHyperlinkRegions.end() );
1211             while( aIter != aEnd )
1212             {
1213                 basegfx::B2DRange const& relRegion( aIter->first );
1214                 aTranslatedRegions.push_back(
1215                     std::make_pair(
1216                         basegfx::B2DRange(
1217                             relRegion.getMinimum() + rOffset,
1218                             relRegion.getMaximum() + rOffset),
1219                         aIter->second) );
1220                 ++aIter;
1221             }
1222 
1223             return aTranslatedRegions;
1224         }
1225 
1226         double DrawShape::getHyperlinkPriority() const
1227         {
1228             return getPriority();
1229         }
1230 
1231 
1232         // AnimatableShape methods
1233         // ======================================================
1234 
1235         void DrawShape::enterAnimationMode()
1236         {
1237             OSL_ENSURE( !maViewShapes.empty(),
1238                         "DrawShape::enterAnimationMode(): called on DrawShape without views" );
1239 
1240             if( mnIsAnimatedCount == 0 )
1241             {
1242                 // notify all ViewShapes, by calling their enterAnimationMode method.
1243                 // We're now entering animation mode
1244                 ::std::for_each( maViewShapes.begin(),
1245                                  maViewShapes.end(),
1246                                  ::boost::mem_fn( &ViewShape::enterAnimationMode ) );
1247             }
1248 
1249             ++mnIsAnimatedCount;
1250         }
1251 
1252         void DrawShape::leaveAnimationMode()
1253         {
1254             OSL_ENSURE( !maViewShapes.empty(),
1255                         "DrawShape::leaveAnimationMode(): called on DrawShape without views" );
1256 
1257             --mnIsAnimatedCount;
1258 
1259             if( mnIsAnimatedCount == 0 )
1260             {
1261                 // notify all ViewShapes, by calling their leaveAnimationMode method.
1262                 // we're now leaving animation mode
1263                 ::std::for_each( maViewShapes.begin(),
1264                                  maViewShapes.end(),
1265                                  ::boost::mem_fn( &ViewShape::leaveAnimationMode ) );
1266             }
1267         }
1268 
1269 
1270         // AttributableShape methods
1271         // ======================================================
1272 
1273         ShapeAttributeLayerSharedPtr DrawShape::createAttributeLayer()
1274         {
1275             // create new layer, with last as its new child
1276             mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) );
1277 
1278             // Update the local state ids to reflect those of the new layer.
1279             updateStateIds();
1280 
1281             return mpAttributeLayer;
1282         }
1283 
1284         bool DrawShape::revokeAttributeLayer( const ShapeAttributeLayerSharedPtr& rLayer )
1285         {
1286             if( !mpAttributeLayer )
1287                 return false; // no layers
1288 
1289             if( mpAttributeLayer == rLayer )
1290             {
1291                 // it's the toplevel layer
1292                 mpAttributeLayer = mpAttributeLayer->getChildLayer();
1293 
1294                 // force content redraw, all state variables have
1295                 // possibly changed
1296                 mbAttributeLayerRevoked = true;
1297 
1298                 return true;
1299             }
1300             else
1301             {
1302                 // pass on to the layer, to try its children
1303                 return mpAttributeLayer->revokeChildLayer( rLayer );
1304             }
1305         }
1306 
1307         ShapeAttributeLayerSharedPtr DrawShape::getTopmostAttributeLayer() const
1308         {
1309             return mpAttributeLayer;
1310         }
1311 
1312         void DrawShape::setVisibility( bool bVisible )
1313         {
1314             if( mbIsVisible != bVisible )
1315             {
1316                 mbIsVisible = bVisible;
1317                 mbForceUpdate = true;
1318             }
1319         }
1320 
1321         const DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier() const
1322         {
1323             return *this;
1324         }
1325 
1326         DocTreeNodeSupplier& DrawShape::getTreeNodeSupplier()
1327         {
1328             return *this;
1329         }
1330 
1331         DocTreeNode DrawShape::getSubsetNode() const
1332         {
1333             ensureVerboseMtfComments();
1334 
1335             // forward to delegate
1336             return maSubsetting.getSubsetNode();
1337         }
1338 
1339         AttributableShapeSharedPtr DrawShape::getSubset( const DocTreeNode& rTreeNode ) const
1340         {
1341             ENSURE_OR_THROW( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0,
1342                               "DrawShape::getSubset(): subset query on shape with apparently no subsets" );
1343 
1344             // forward to delegate
1345             return maSubsetting.getSubsetShape( rTreeNode );
1346         }
1347 
1348         bool DrawShape::createSubset( AttributableShapeSharedPtr& 	o_rSubset,
1349                                       const DocTreeNode& 			rTreeNode )
1350         {
1351             ENSURE_OR_THROW( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0,
1352                               "DrawShape::createSubset(): subset query on shape with apparently no subsets" );
1353 
1354             // subset shape already created for this DocTreeNode?
1355             AttributableShapeSharedPtr pSubset( maSubsetting.getSubsetShape( rTreeNode ) );
1356 
1357             // when true, this method has created a new subset
1358             // DrawShape
1359             bool bNewlyCreated( false );
1360 
1361             if( pSubset )
1362             {
1363                 o_rSubset = pSubset;
1364 
1365                 // reusing existing subset
1366             }
1367             else
1368             {
1369                 // not yet created, init entry
1370                 o_rSubset.reset( new DrawShape( *this,
1371                                                 rTreeNode,
1372                                                 // TODO(Q3): That's a
1373                                                 // hack. We assume
1374                                                 // that start and end
1375                                                 // index will always
1376                                                 // be less than 65535
1377                                                 mnPriority +
1378                                                 rTreeNode.getStartIndex()/double(SAL_MAX_INT16) ));
1379 
1380                 bNewlyCreated = true; // subset newly created
1381             }
1382 
1383             // always register shape at DrawShapeSubsetting, to keep
1384             // refcount up-to-date
1385             maSubsetting.addSubsetShape( o_rSubset );
1386 
1387             // flush bounds cache
1388             maCurrentShapeUnitBounds.reset();
1389 
1390             return bNewlyCreated;
1391         }
1392 
1393         bool DrawShape::revokeSubset( const AttributableShapeSharedPtr& rShape )
1394         {
1395             ENSURE_OR_THROW( (mnCurrMtfLoadFlags & MTF_LOAD_VERBOSE_COMMENTS) != 0,
1396                               "DrawShape::createSubset(): subset query on shape with apparently no subsets" );
1397 
1398             // flush bounds cache
1399             maCurrentShapeUnitBounds.reset();
1400 
1401             // forward to delegate
1402             if( maSubsetting.revokeSubsetShape( rShape ) )
1403             {
1404                 // force redraw, our content has possibly changed (as
1405                 // one of the subsets now display within our shape
1406                 // again).
1407                 mbForceUpdate = true;
1408 
1409                 // #i47428# TEMP FIX: synchronize visibility of subset
1410                 // with parent.
1411 
1412                 // TODO(F3): Remove here, and implement
1413                 // TEXT_ONLY/BACKGROUND_ONLY with the proverbial
1414                 // additional level of indirection: create a
1415                 // persistent subset, containing all text/only the
1416                 // background respectively. From _that_ object,
1417                 // generate the temporary character subset shapes.
1418                 const ShapeAttributeLayerSharedPtr& rAttrLayer(
1419                     rShape->getTopmostAttributeLayer() );
1420                 if( rAttrLayer &&
1421                     rAttrLayer->isVisibilityValid() &&
1422                     rAttrLayer->getVisibility() != isVisible() )
1423                 {
1424                     const bool bVisibility( rAttrLayer->getVisibility() );
1425 
1426                     // visibilities differ - adjust ours, then
1427                     if( mpAttributeLayer )
1428                         mpAttributeLayer->setVisibility( bVisibility );
1429                     else
1430                         mbIsVisible = bVisibility;
1431                 }
1432 
1433                 // END TEMP FIX
1434 
1435                 return true;
1436             }
1437 
1438             return false;
1439         }
1440 
1441         sal_Int32 DrawShape::getNumberOfTreeNodes( DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1442         {
1443             ensureVerboseMtfComments();
1444 
1445             return maSubsetting.getNumberOfTreeNodes( eNodeType );
1446         }
1447 
1448         DocTreeNode DrawShape::getTreeNode( sal_Int32				nNodeIndex,
1449                                             DocTreeNode::NodeType	eNodeType ) const // throw ShapeLoadFailedException
1450         {
1451             ensureVerboseMtfComments();
1452 
1453             if ( hasHyperlinks())
1454             {
1455                 prepareHyperlinkIndices();
1456             }
1457 
1458             return maSubsetting.getTreeNode( nNodeIndex, eNodeType );
1459         }
1460 
1461         sal_Int32 DrawShape::getNumberOfSubsetTreeNodes	( const DocTreeNode& 	rParentNode,
1462                                                           DocTreeNode::NodeType eNodeType ) const // throw ShapeLoadFailedException
1463         {
1464             ensureVerboseMtfComments();
1465 
1466             return maSubsetting.getNumberOfSubsetTreeNodes( rParentNode, eNodeType );
1467         }
1468 
1469         DocTreeNode DrawShape::getSubsetTreeNode( const DocTreeNode& 	rParentNode,
1470                                                   sal_Int32				nNodeIndex,
1471                                                   DocTreeNode::NodeType	eNodeType ) const // throw ShapeLoadFailedException
1472         {
1473             ensureVerboseMtfComments();
1474 
1475             return maSubsetting.getSubsetTreeNode( rParentNode, nNodeIndex, eNodeType );
1476         }
1477     }
1478 }
1479