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