/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// using namespace com::sun::star; ////////////////////////////////////////////////////////////////////////////// namespace { // animated extractor // Necessary to filter a sequence of animated primitives from // a sequence of primitives to find out if animated or not. The decision for // what to decompose is hard-coded and only done for knowingly animated primitives // to not decompose too deeply and unnecessarily. This implies that the list // which is view-specific needs to be expanded by hand when new animated objects // are added. This may eventually be changed to a dynamically configurable approach // if necessary. class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D { protected: // the found animated primitives drawinglayer::primitive2d::Primitive2DSequence maPrimitive2DSequence; // bitfield // text animation allowed? unsigned mbTextAnimationAllowed : 1; // graphic animation allowed? unsigned mbGraphicAnimationAllowed : 1; // as tooling, the process() implementation takes over API handling and calls this // virtual render method when the primitive implementation is BasePrimitive2D-based. virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate); public: AnimatedExtractingProcessor2D( const drawinglayer::geometry::ViewInformation2D& rViewInformation, bool bTextAnimationAllowed, bool bGraphicAnimationAllowed); virtual ~AnimatedExtractingProcessor2D(); // data access const drawinglayer::primitive2d::Primitive2DSequence& getPrimitive2DSequence() const { return maPrimitive2DSequence; } bool isTextAnimationAllowed() const { return mbTextAnimationAllowed; } bool isGraphicAnimationAllowed() const { return mbGraphicAnimationAllowed; } }; AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D( const drawinglayer::geometry::ViewInformation2D& rViewInformation, bool bTextAnimationAllowed, bool bGraphicAnimationAllowed) : drawinglayer::processor2d::BaseProcessor2D(rViewInformation), maPrimitive2DSequence(), mbTextAnimationAllowed(bTextAnimationAllowed), mbGraphicAnimationAllowed(bGraphicAnimationAllowed) { } AnimatedExtractingProcessor2D::~AnimatedExtractingProcessor2D() { } void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) { // known implementation, access directly switch(rCandidate.getPrimitive2DID()) { // add and accept animated primitives directly, no need to decompose case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D : case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D : case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D : { const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate); if((rSwitchPrimitive.isTextAnimation() && isTextAnimationAllowed()) || (rSwitchPrimitive.isGraphicAnimation() && isGraphicAnimationAllowed())) { const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate)); drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(maPrimitive2DSequence, xReference); } break; } // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D // which then produces the animation infos (all when used/needed) case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D : case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D : // decompose SdrObjects with evtl. animated text case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D : case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D : case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D : case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D : case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D : case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D : case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D : case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D : // decompose evtl. animated text contained in MaskPrimitive2D // or group rimitives case PRIMITIVE2D_ID_MASKPRIMITIVE2D : case PRIMITIVE2D_ID_GROUPPRIMITIVE2D : { process(rCandidate.get2DDecomposition(getViewInformation2D())); break; } default : { // nothing to do for the rest break; } } } } // end of anonymous namespace ////////////////////////////////////////////////////////////////////////////// namespace sdr { namespace contact { ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact) : mrObjectContact(rObjectContact), mrViewContact(rViewContact), maObjectRange(), mxPrimitive2DSequence(), mpPrimitiveAnimation(0), mbLazyInvalidate(false) { // make the ViewContact remember me mrViewContact.AddViewObjectContact(*this); // make the ObjectContact remember me mrObjectContact.AddViewObjectContact(*this); } ViewObjectContact::~ViewObjectContact() { // invalidate in view if(!maObjectRange.isEmpty()) { GetObjectContact().InvalidatePartOfView(maObjectRange); } // delete PrimitiveAnimation if(mpPrimitiveAnimation) { delete mpPrimitiveAnimation; mpPrimitiveAnimation = 0; } // take care of remebered ObjectContact. Remove from // OC first. The VC removal (below) CAN trigger a StopGettingViewed() // which (depending of it's implementation) may destroy other OCs. This // can trigger the deletion of the helper OC of a page visualising object // which IS the OC of this object. Eventually StopGettingViewed() needs // to get asynchron later GetObjectContact().RemoveViewObjectContact(*this); // take care of remebered ViewContact GetViewContact().RemoveViewObjectContact(*this); } const basegfx::B2DRange& ViewObjectContact::getObjectRange() const { if(maObjectRange.isEmpty()) { // if range is not computed (new or LazyInvalidate objects), force it const DisplayInfo aDisplayInfo; const drawinglayer::primitive2d::Primitive2DSequence xSequence(getPrimitive2DSequence(aDisplayInfo)); if(xSequence.hasElements()) { const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); const_cast< ViewObjectContact* >(this)->maObjectRange = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xSequence, rViewInformation2D); } } return maObjectRange; } void ViewObjectContact::ActionChanged() { if(!mbLazyInvalidate) { // set local flag mbLazyInvalidate = true; // force ObjectRange getObjectRange(); if(!maObjectRange.isEmpty()) { // invalidate current valid range GetObjectContact().InvalidatePartOfView(maObjectRange); // reset ObjectRange, it needs to be recalculated maObjectRange.reset(); } // register at OC for lazy invalidate GetObjectContact().setLazyInvalidate(*this); } } void ViewObjectContact::triggerLazyInvalidate() { if(mbLazyInvalidate) { // reset flag mbLazyInvalidate = false; // force ObjectRange getObjectRange(); if(!maObjectRange.isEmpty()) { // invalidate current valid range GetObjectContact().InvalidatePartOfView(maObjectRange); } } } // Take some action when new objects are inserted void ViewObjectContact::ActionChildInserted(ViewContact& rChild) { // force creation of the new VOC and trigger it's refresh, so it // will take part in LazyInvalidate immediately rChild.GetViewObjectContact(GetObjectContact()).ActionChanged(); // forward action to ObjectContact // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact()); // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange()); } void ViewObjectContact::checkForPrimitive2DAnimations() { // remove old one if(mpPrimitiveAnimation) { delete mpPrimitiveAnimation; mpPrimitiveAnimation = 0; } // check for animated primitives if(mxPrimitive2DSequence.hasElements()) { const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed()); const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed()); if(bTextAnimationAllowed || bGraphicAnimationAllowed) { AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(), bTextAnimationAllowed, bGraphicAnimationAllowed); aAnimatedExtractor.process(mxPrimitive2DSequence); if(aAnimatedExtractor.getPrimitive2DSequence().hasElements()) { // dervied primitiveList is animated, setup new PrimitiveAnimation mpPrimitiveAnimation = new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.getPrimitive2DSequence()); } } } } drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const { // get the view-independent Primitive from the viewContact drawinglayer::primitive2d::Primitive2DSequence xRetval(GetViewContact().getViewIndependentPrimitive2DSequence()); if(xRetval.hasElements()) { // handle GluePoint if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible()) { const drawinglayer::primitive2d::Primitive2DSequence xGlue(GetViewContact().createGluePointPrimitive2DSequence()); if(xGlue.hasElements()) { drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, xGlue); } } // handle ghosted if(isPrimitiveGhosted(rDisplayInfo)) { const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0); const basegfx::BColorModifier aBColorModifier(aRGBWhite, 0.5, basegfx::BCOLORMODIFYMODE_INTERPOLATE); const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::ModifiedColorPrimitive2D(xRetval, aBColorModifier)); xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); } } return xRetval; } drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const { drawinglayer::primitive2d::Primitive2DSequence xNewPrimitiveSequence; // take care of redirectors and create new list ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector(); if(pRedirector) { xNewPrimitiveSequence = pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo); } else { xNewPrimitiveSequence = createPrimitive2DSequence(rDisplayInfo); } // local up-to-date checks. New list different from local one? if(!drawinglayer::primitive2d::arePrimitive2DSequencesEqual(mxPrimitive2DSequence, xNewPrimitiveSequence)) { // has changed, copy content const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = xNewPrimitiveSequence; // check for animated stuff const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations(); // always update object range when PrimitiveSequence changes const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); const_cast< ViewObjectContact* >(this)->maObjectRange = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(mxPrimitive2DSequence, rViewInformation2D); } // return current Primitive2DSequence return mxPrimitive2DSequence; } bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const { // default: always visible return true; } bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const { // default: standard check return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive()); } drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo) const { drawinglayer::primitive2d::Primitive2DSequence xRetval; // check model-view visibility if(isPrimitiveVisible(rDisplayInfo)) { xRetval = getPrimitive2DSequence(rDisplayInfo); if(xRetval.hasElements()) { // get ranges const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); const basegfx::B2DRange aObjectRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xRetval, rViewInformation2D)); const basegfx::B2DRange aViewRange(rViewInformation2D.getViewport()); // check geometrical visibility if(!aViewRange.isEmpty() && !aViewRange.overlaps(aObjectRange)) { // not visible, release xRetval.realloc(0); } } } return xRetval; } drawinglayer::primitive2d::Primitive2DSequence ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo) const { const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount()); drawinglayer::primitive2d::Primitive2DSequence xSeqRetval; for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) { const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact())); drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xSeqRetval, rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo)); } return xSeqRetval; } } // end of namespace contact } // end of namespace sdr /* vim:set shiftwidth=4 softtabstop=4 expandtab: */