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_svx.hxx" 26 #include <svx/sdr/contact/viewobjectcontact.hxx> 27 #include <svx/sdr/contact/viewcontact.hxx> 28 #include <svx/sdr/contact/objectcontact.hxx> 29 #include <svx/sdr/contact/displayinfo.hxx> 30 #include <vcl/region.hxx> 31 #include <svx/sdr/animation/objectanimator.hxx> 32 #include <svx/sdr/animation/animationstate.hxx> 33 #include <svx/sdr/contact/viewobjectcontactredirector.hxx> 34 #include <basegfx/numeric/ftools.hxx> 35 #include <basegfx/color/bcolor.hxx> 36 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> 37 #include <basegfx/tools/canvastools.hxx> 38 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx> 39 #include <drawinglayer/processor2d/baseprocessor2d.hxx> 40 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> 41 #include <svx/sdr/contact/viewobjectcontactredirector.hxx> 42 43 ////////////////////////////////////////////////////////////////////////////// 44 45 using namespace com::sun::star; 46 47 ////////////////////////////////////////////////////////////////////////////// 48 49 namespace 50 { 51 // animated extractor 52 53 // Necessary to filter a sequence of animated primitives from 54 // a sequence of primitives to find out if animated or not. The decision for 55 // what to decompose is hard-coded and only done for knowingly animated primitives 56 // to not decompose too deeply and unnecessarily. This implies that the list 57 // which is view-specific needs to be expanded by hand when new animated objects 58 // are added. This may eventually be changed to a dynamically configurable approach 59 // if necessary. 60 class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D 61 { 62 protected: 63 // the found animated primitives 64 drawinglayer::primitive2d::Primitive2DSequence maPrimitive2DSequence; 65 66 // bitfield 67 // text animation allowed? 68 unsigned mbTextAnimationAllowed : 1; 69 70 // graphic animation allowed? 71 unsigned mbGraphicAnimationAllowed : 1; 72 73 // as tooling, the process() implementation takes over API handling and calls this 74 // virtual render method when the primitive implementation is BasePrimitive2D-based. 75 virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate); 76 77 public: 78 AnimatedExtractingProcessor2D( 79 const drawinglayer::geometry::ViewInformation2D& rViewInformation, 80 bool bTextAnimationAllowed, 81 bool bGraphicAnimationAllowed); 82 virtual ~AnimatedExtractingProcessor2D(); 83 84 // data access 85 const drawinglayer::primitive2d::Primitive2DSequence& getPrimitive2DSequence() const { return maPrimitive2DSequence; } 86 bool isTextAnimationAllowed() const { return mbTextAnimationAllowed; } 87 bool isGraphicAnimationAllowed() const { return mbGraphicAnimationAllowed; } 88 }; 89 90 AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D( 91 const drawinglayer::geometry::ViewInformation2D& rViewInformation, 92 bool bTextAnimationAllowed, 93 bool bGraphicAnimationAllowed) 94 : drawinglayer::processor2d::BaseProcessor2D(rViewInformation), 95 maPrimitive2DSequence(), 96 mbTextAnimationAllowed(bTextAnimationAllowed), 97 mbGraphicAnimationAllowed(bGraphicAnimationAllowed) 98 { 99 } 100 101 AnimatedExtractingProcessor2D::~AnimatedExtractingProcessor2D() 102 { 103 } 104 105 void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) 106 { 107 // known implementation, access directly 108 switch(rCandidate.getPrimitive2DID()) 109 { 110 // add and accept animated primitives directly, no need to decompose 111 case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D : 112 case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D : 113 case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D : 114 { 115 const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate); 116 117 if((rSwitchPrimitive.isTextAnimation() && isTextAnimationAllowed()) 118 || (rSwitchPrimitive.isGraphicAnimation() && isGraphicAnimationAllowed())) 119 { 120 const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate)); 121 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(maPrimitive2DSequence, xReference); 122 } 123 break; 124 } 125 126 // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D 127 // which then produces the animation infos (all when used/needed) 128 case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D : 129 case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D : 130 131 // decompose SdrObjects with evtl. animated text 132 case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D : 133 case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D : 134 case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D : 135 case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D : 136 case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D : 137 case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D : 138 case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D : 139 case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D : 140 141 // decompose evtl. animated text contained in MaskPrimitive2D 142 // or group rimitives 143 case PRIMITIVE2D_ID_MASKPRIMITIVE2D : 144 case PRIMITIVE2D_ID_GROUPPRIMITIVE2D : 145 { 146 process(rCandidate.get2DDecomposition(getViewInformation2D())); 147 break; 148 } 149 150 default : 151 { 152 // nothing to do for the rest 153 break; 154 } 155 } 156 } 157 } // end of anonymous namespace 158 159 ////////////////////////////////////////////////////////////////////////////// 160 161 namespace sdr 162 { 163 namespace contact 164 { 165 ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact) 166 : mrObjectContact(rObjectContact), 167 mrViewContact(rViewContact), 168 maObjectRange(), 169 mxPrimitive2DSequence(), 170 mpPrimitiveAnimation(0), 171 mbLazyInvalidate(false) 172 { 173 // make the ViewContact remember me 174 mrViewContact.AddViewObjectContact(*this); 175 176 // make the ObjectContact remember me 177 mrObjectContact.AddViewObjectContact(*this); 178 } 179 180 ViewObjectContact::~ViewObjectContact() 181 { 182 // invalidate in view 183 if(!maObjectRange.isEmpty()) 184 { 185 GetObjectContact().InvalidatePartOfView(maObjectRange); 186 } 187 188 // delete PrimitiveAnimation 189 if(mpPrimitiveAnimation) 190 { 191 delete mpPrimitiveAnimation; 192 mpPrimitiveAnimation = 0; 193 } 194 195 // take care of remebered ObjectContact. Remove from 196 // OC first. The VC removal (below) CAN trigger a StopGettingViewed() 197 // which (depending of it's implementation) may destroy other OCs. This 198 // can trigger the deletion of the helper OC of a page visualising object 199 // which IS the OC of this object. Eventually StopGettingViewed() needs 200 // to get asynchron later 201 GetObjectContact().RemoveViewObjectContact(*this); 202 203 // take care of remebered ViewContact 204 GetViewContact().RemoveViewObjectContact(*this); 205 } 206 207 const basegfx::B2DRange& ViewObjectContact::getObjectRange() const 208 { 209 if(maObjectRange.isEmpty()) 210 { 211 // if range is not computed (new or LazyInvalidate objects), force it 212 const DisplayInfo aDisplayInfo; 213 const drawinglayer::primitive2d::Primitive2DSequence xSequence(getPrimitive2DSequence(aDisplayInfo)); 214 215 if(xSequence.hasElements()) 216 { 217 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); 218 const_cast< ViewObjectContact* >(this)->maObjectRange = 219 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xSequence, rViewInformation2D); 220 } 221 } 222 223 return maObjectRange; 224 } 225 226 void ViewObjectContact::ActionChanged() 227 { 228 if(!mbLazyInvalidate) 229 { 230 // set local flag 231 mbLazyInvalidate = true; 232 233 // force ObjectRange 234 getObjectRange(); 235 236 if(!maObjectRange.isEmpty()) 237 { 238 // invalidate current valid range 239 GetObjectContact().InvalidatePartOfView(maObjectRange); 240 241 // reset ObjectRange, it needs to be recalculated 242 maObjectRange.reset(); 243 } 244 245 // register at OC for lazy invalidate 246 GetObjectContact().setLazyInvalidate(*this); 247 } 248 } 249 250 void ViewObjectContact::triggerLazyInvalidate() 251 { 252 if(mbLazyInvalidate) 253 { 254 // reset flag 255 mbLazyInvalidate = false; 256 257 // force ObjectRange 258 getObjectRange(); 259 260 if(!maObjectRange.isEmpty()) 261 { 262 // invalidate current valid range 263 GetObjectContact().InvalidatePartOfView(maObjectRange); 264 } 265 } 266 } 267 268 // Take some action when new objects are inserted 269 void ViewObjectContact::ActionChildInserted(ViewContact& rChild) 270 { 271 // force creation of the new VOC and trigger it's refresh, so it 272 // will take part in LazyInvalidate immediately 273 rChild.GetViewObjectContact(GetObjectContact()).ActionChanged(); 274 275 // forward action to ObjectContact 276 // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact()); 277 // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange()); 278 } 279 280 void ViewObjectContact::checkForPrimitive2DAnimations() 281 { 282 // remove old one 283 if(mpPrimitiveAnimation) 284 { 285 delete mpPrimitiveAnimation; 286 mpPrimitiveAnimation = 0; 287 } 288 289 // check for animated primitives 290 if(mxPrimitive2DSequence.hasElements()) 291 { 292 const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed()); 293 const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed()); 294 295 if(bTextAnimationAllowed || bGraphicAnimationAllowed) 296 { 297 AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(), 298 bTextAnimationAllowed, bGraphicAnimationAllowed); 299 aAnimatedExtractor.process(mxPrimitive2DSequence); 300 301 if(aAnimatedExtractor.getPrimitive2DSequence().hasElements()) 302 { 303 // dervied primitiveList is animated, setup new PrimitiveAnimation 304 mpPrimitiveAnimation = new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence()); 305 } 306 } 307 } 308 } 309 310 drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const 311 { 312 // get the view-independent Primitive from the viewContact 313 drawinglayer::primitive2d::Primitive2DSequence xRetval(GetViewContact().getViewIndependentPrimitive2DSequence()); 314 315 if(xRetval.hasElements()) 316 { 317 // handle GluePoint 318 if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible()) 319 { 320 const drawinglayer::primitive2d::Primitive2DSequence xGlue(GetViewContact().createGluePointPrimitive2DSequence()); 321 322 if(xGlue.hasElements()) 323 { 324 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, xGlue); 325 } 326 } 327 328 // handle ghosted 329 if(isPrimitiveGhosted(rDisplayInfo)) 330 { 331 const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0); 332 const basegfx::BColorModifier aBColorModifier(aRGBWhite, 0.5, basegfx::BCOLORMODIFYMODE_INTERPOLATE); 333 const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::ModifiedColorPrimitive2D(xRetval, aBColorModifier)); 334 xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); 335 } 336 } 337 338 return xRetval; 339 } 340 341 drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const 342 { 343 drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence; 344 345 // take care of redirectors and create new list 346 ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector(); 347 348 if(pRedirector) 349 { 350 xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo); 351 } 352 else 353 { 354 xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo); 355 } 356 357 // local up-to-date checks. New list different from local one? 358 if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence)) 359 { 360 // has changed, copy content 361 const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence; 362 363 // check for animated stuff 364 const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations(); 365 366 // always update object range when PrimitiveSequence changes 367 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); 368 const_cast< ViewObjectContact* >(this)->maObjectRange = 369 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D); 370 } 371 372 // return current Primitive2DSequence 373 return mxPrimitive2DSequence; 374 } 375 376 bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const 377 { 378 // default: always visible 379 return true; 380 } 381 382 bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const 383 { 384 // default: standard check 385 return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive()); 386 } 387 388 drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const 389 { 390 drawinglayer::primitive2d::Primitive2DSequence xRetval; 391 392 // check model-view visibility 393 if(isPrimitiveVisible(rDisplayInfo)) 394 { 395 xRetval = getPrimitive2DSequence(rDisplayInfo); 396 397 if(xRetval.hasElements()) 398 { 399 // get ranges 400 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); 401 const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D)); 402 const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport()); 403 404 // check geometrical visibility 405 if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange)) 406 { 407 // not visible, release 408 xRetval.realloc(0); 409 } 410 } 411 } 412 413 return xRetval; 414 } 415 416 drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const 417 { 418 const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount()); 419 drawinglayer::primitive2d::Primitive2DSequence xSeqRetval; 420 421 for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) 422 { 423 const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact())); 424 425 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo)); 426 } 427 428 return xSeqRetval; 429 } 430 } // end of namespace contact 431 } // end of namespace sdr 432 433 ////////////////////////////////////////////////////////////////////////////// 434 // eof 435