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