/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: SlsPageObjectViewObjectContact.cxx,v $ * $Revision: 1.23 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "precompiled_sd.hxx" #include "view/SlsPageObjectViewObjectContact.hxx" #include "controller/SlsProperties.hxx" #include "view/SlideSorterView.hxx" #include "view/SlsPageObjectViewContact.hxx" #include "view/SlsPageObject.hxx" #include "view/SlsFontProvider.hxx" #include "model/SlsPageDescriptor.hxx" #include "cache/SlsPageCache.hxx" #include "cache/SlsPageCacheManager.hxx" #include "res_bmp.hrc" #include "tools/IconCache.hxx" #include "PreviewRenderer.hxx" #include "sdpage.hxx" #include "sdresid.hxx" #include "glob.hrc" #include "drawdoc.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::sdr::contact; using namespace ::sd::slidesorter::model; namespace sd { namespace slidesorter { namespace view { const sal_Int32 PageObjectViewObjectContact::mnSelectionIndicatorOffset = 2; const sal_Int32 PageObjectViewObjectContact::mnSelectionIndicatorThickness = 3; const sal_Int32 PageObjectViewObjectContact::mnFocusIndicatorOffset = 3; const sal_Int32 PageObjectViewObjectContact::mnFadeEffectIndicatorOffset = 9; const sal_Int32 PageObjectViewObjectContact::mnFadeEffectIndicatorSize = 14; const sal_Int32 PageObjectViewObjectContact::mnPageNumberOffset = 9; const sal_Int32 PageObjectViewObjectContact::mnMouseOverEffectOffset = 3; const sal_Int32 PageObjectViewObjectContact::mnMouseOverEffectThickness = 1; PageObjectViewObjectContact::PageObjectViewObjectContact ( ObjectContact& rObjectContact, ViewContact& rViewContact, const ::boost::shared_ptr& rpCache, const ::boost::shared_ptr& rpProperties) : ViewObjectContactOfPageObj(rObjectContact, rViewContact), mbInDestructor(false), mbIsBackgroundColorUpdatePending(true), mxCurrentPageContents(), mpCache(rpCache), mpProperties(rpProperties), maBackgroundColor() { SharedPageDescriptor pDescriptor (GetPageDescriptor()); OSL_ASSERT(pDescriptor.get()!=NULL); if (pDescriptor.get() != NULL) pDescriptor->SetViewObjectContact(this); } PageObjectViewObjectContact::~PageObjectViewObjectContact (void) { mbInDestructor = true; GetPageDescriptor()->SetViewObjectContact(NULL); if (mpCache.get() != NULL) { const SdrPage* pPage = GetPage(); if(pPage) { mpCache->ReleasePreviewBitmap(GetPage()); } } } void PageObjectViewObjectContact::SetCache (const ::boost::shared_ptr& rpCache) { mpCache = rpCache; } Rectangle PageObjectViewObjectContact::GetBoundingBox ( OutputDevice& rDevice, BoundingBoxType eType, CoordinateSystem eCoordinateSystem) const { // Most of the bounding boxes are based on the bounding box of the preview. // SdrPageObj is a SdrObject, so use SdrObject::aOutRect as model data const PageObjectViewContact& rPaObVOC(static_cast(GetViewContact())); Rectangle aBoundingBox(rPaObVOC.GetPageObject().GetLastBoundRect()); CoordinateSystem eCurrentCoordinateSystem (ModelCoordinateSystem); switch(eType) { case PageObjectBoundingBox: { const SvBorder aPageDescriptorBorder(GetPageDescriptor()->GetModelBorder()); aBoundingBox.Left() -= aPageDescriptorBorder.Left(); aBoundingBox.Top() -= aPageDescriptorBorder.Top(); aBoundingBox.Right() += aPageDescriptorBorder.Right(); aBoundingBox.Bottom() += aPageDescriptorBorder.Bottom(); break; } case PreviewBoundingBox: { // The aBoundingBox already has the right value. break; } case MouseOverIndicatorBoundingBox: { const sal_Int32 nBorderWidth (mnMouseOverEffectOffset+mnMouseOverEffectThickness); const Size aBorderSize (rDevice.PixelToLogic(Size(nBorderWidth,nBorderWidth))); aBoundingBox.Left() -= aBorderSize.Width(); aBoundingBox.Top() -= aBorderSize.Height(); aBoundingBox.Right() += aBorderSize.Width(); aBoundingBox.Bottom() += aBorderSize.Height(); break; } case FocusIndicatorBoundingBox: { const sal_Int32 nBorderWidth (mnFocusIndicatorOffset+1); const Size aBorderSize (rDevice.PixelToLogic(Size(nBorderWidth,nBorderWidth))); aBoundingBox.Left() -= aBorderSize.Width(); aBoundingBox.Top() -= aBorderSize.Height(); aBoundingBox.Right() += aBorderSize.Width(); aBoundingBox.Bottom() += aBorderSize.Height(); break; } case SelectionIndicatorBoundingBox: { const sal_Int32 nBorderWidth(mnSelectionIndicatorOffset+mnSelectionIndicatorThickness); const Size aBorderSize (rDevice.PixelToLogic(Size(nBorderWidth,nBorderWidth))); aBoundingBox.Left() -= aBorderSize.Width(); aBoundingBox.Top() -= aBorderSize.Height(); aBoundingBox.Right() += aBorderSize.Width(); aBoundingBox.Bottom() += aBorderSize.Height(); break; } case PageNumberBoundingBox: { Size aModelOffset = rDevice.PixelToLogic(Size(mnPageNumberOffset,mnPageNumberOffset)); Size aNumberSize (GetPageDescriptor()->GetPageNumberAreaModelSize()); aBoundingBox = Rectangle ( Point ( aBoundingBox.Left() - aModelOffset.Width() - aNumberSize.Width(), aBoundingBox.Top()), aNumberSize); break; } case NameBoundingBox: break; case FadeEffectIndicatorBoundingBox: Size aModelOffset = rDevice.PixelToLogic(Size (0, mnFadeEffectIndicatorOffset)); // Flush left just outside the selection rectangle. aBoundingBox = Rectangle ( Point ( aBoundingBox.Left(), aBoundingBox.Bottom() + aModelOffset.Height() ), rDevice.PixelToLogic ( IconCache::Instance().GetIcon(BMP_FADE_EFFECT_INDICATOR).GetSizePixel()) ); break; } // Make sure the bounding box uses the requested coordinate system. if (eCurrentCoordinateSystem != eCoordinateSystem) { if (eCoordinateSystem == ModelCoordinateSystem) aBoundingBox = Rectangle( rDevice.PixelToLogic(aBoundingBox.TopLeft()), rDevice.PixelToLogic(aBoundingBox.GetSize())); else aBoundingBox = Rectangle( rDevice.LogicToPixel(aBoundingBox.TopLeft()), rDevice.LogicToPixel(aBoundingBox.GetSize())); } return aBoundingBox; } /////////////////////////////////////////////////////////////////////////////////////////////// // example implementation for primitive usage for PageObjectViewObjectContact } } } // end of namespace ::sd::slidesorter::view #include #include #include #include #include #include #include #include #include #include namespace sd { namespace slidesorter { namespace view { /////////////////////////////////////////////////////////////////////////////////////////////// // All primitives for SdrPageObject visualisation are based on one range which describes // the size of the inner rectangle for PagePreview visualisation. Use a common implementation // class for all derived SdPageObjectPrimitives. The SdPageObjectBasePrimitive itself // is pure virtual class SdPageObjectBasePrimitive : public drawinglayer::primitive2d::BasePrimitive2D { private: // the inner range of the SdPageObject visualisation basegfx::B2DRange maRange; public: // constructor and destructor SdPageObjectBasePrimitive(const basegfx::B2DRange& rRange); virtual ~SdPageObjectBasePrimitive(); // data access const basegfx::B2DRange& getPageObjectRange() const { return maRange; } // compare operator virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const; }; SdPageObjectBasePrimitive::SdPageObjectBasePrimitive(const basegfx::B2DRange& rRange) : drawinglayer::primitive2d::BasePrimitive2D(), maRange(rRange) { } SdPageObjectBasePrimitive::~SdPageObjectBasePrimitive() { } bool SdPageObjectBasePrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const { if(drawinglayer::primitive2d::BasePrimitive2D::operator==(rPrimitive)) { const SdPageObjectBasePrimitive& rCompare = static_cast< const SdPageObjectBasePrimitive& >(rPrimitive); return (getPageObjectRange() == rCompare.getPageObjectRange()); } return false; } /////////////////////////////////////////////////////////////////////////////////////////////// // SdPageObjectPrimitive for selected visualisation class SdPageObjectPageBitmapPrimitive : public SdPageObjectBasePrimitive { private: // the bitmap containing the PagePreview BitmapEx maBitmapEx; protected: // method which is to be used to implement the local decomposition of a 2D primitive. virtual drawinglayer::primitive2d::Primitive2DSequence createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const; public: // constructor and destructor SdPageObjectPageBitmapPrimitive( const basegfx::B2DRange& rRange, const BitmapEx& rBitmapEx); ~SdPageObjectPageBitmapPrimitive(); // data access const BitmapEx& getBitmapEx() const { return maBitmapEx; } // compare operator virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const; // provide unique ID DeclPrimitrive2DIDBlock() }; drawinglayer::primitive2d::Primitive2DSequence SdPageObjectPageBitmapPrimitive::createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const { // add bitmap primitive // to avoid scaling, use the Bitmap pixel size as primitive size basegfx::B2DHomMatrix aBitmapTransform; const Size aBitmapSize(getBitmapEx().GetSizePixel()); const basegfx::B2DVector aBitmapSizeLogic(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1)); // short form for scale and translate transformation aBitmapTransform.set(0L, 0L, aBitmapSizeLogic.getX()); aBitmapTransform.set(1L, 1L, aBitmapSizeLogic.getY()); aBitmapTransform.set(0L, 2L, getPageObjectRange().getMinX()); aBitmapTransform.set(1L, 2L, getPageObjectRange().getMinY()); // add a BitmapPrimitive2D to the result const drawinglayer::primitive2d::Primitive2DReference xReference( new drawinglayer::primitive2d::BitmapPrimitive2D(getBitmapEx(), aBitmapTransform)); return drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); } SdPageObjectPageBitmapPrimitive::SdPageObjectPageBitmapPrimitive( const basegfx::B2DRange& rRange, const BitmapEx& rBitmapEx) : SdPageObjectBasePrimitive(rRange), maBitmapEx(rBitmapEx) { } SdPageObjectPageBitmapPrimitive::~SdPageObjectPageBitmapPrimitive() { } bool SdPageObjectPageBitmapPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const { if(SdPageObjectBasePrimitive::operator==(rPrimitive)) { const SdPageObjectPageBitmapPrimitive& rCompare = static_cast< const SdPageObjectPageBitmapPrimitive& >(rPrimitive); return (getBitmapEx() == rCompare.getBitmapEx()); } return false; } ImplPrimitrive2DIDBlock(SdPageObjectPageBitmapPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTPAGEBITMAPPRIMITIVE) /////////////////////////////////////////////////////////////////////////////////////////////// // SdPageObjectPrimitive for selected visualisation class SdPageObjectSelectPrimitive : public SdPageObjectBasePrimitive { private: /// Gap between border of page object and inside of selection rectangle. static const sal_Int32 mnSelectionIndicatorOffset; /// Thickness of the selection rectangle. static const sal_Int32 mnSelectionIndicatorThickness; protected: // method which is to be used to implement the local decomposition of a 2D primitive. virtual drawinglayer::primitive2d::Primitive2DSequence createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const; public: // constructor and destructor SdPageObjectSelectPrimitive(const basegfx::B2DRange& rRange); ~SdPageObjectSelectPrimitive(); // provide unique ID DeclPrimitrive2DIDBlock() }; const sal_Int32 SdPageObjectSelectPrimitive::mnSelectionIndicatorOffset(1); const sal_Int32 SdPageObjectSelectPrimitive::mnSelectionIndicatorThickness(3); drawinglayer::primitive2d::Primitive2DSequence SdPageObjectSelectPrimitive::createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const { drawinglayer::primitive2d::Primitive2DSequence xRetval(2); // since old Width/Height calculations always added a single pixel value, // it is necessary to create a inner range which is one display unit less // at the bottom right. const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); const basegfx::B2DRange aAdaptedInnerRange( getPageObjectRange().getMinX(), getPageObjectRange().getMinY(), getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY()); // PaintSelectionIndicator replacement. Grow by offset first basegfx::B2DRange aDiscreteOuterRange(aAdaptedInnerRange); aDiscreteOuterRange.grow(mnSelectionIndicatorOffset * aDiscretePixel.getX()); // remeber inner border. Make it one bigger in top left since polygons // do not paint their lower-right corners. Since this is the inner polygon, // the top-left corders are the ones to grow here const basegfx::B2DRange aDiscreteInnerRange( aDiscreteOuterRange.getMinimum() + aDiscretePixel, aDiscreteOuterRange.getMaximum()); // grow by line width aDiscreteOuterRange.grow((mnSelectionIndicatorThickness - 1) * aDiscretePixel.getX()); // create a PolyPolygon from those ranges. For the outer polygon, round edges by // giving a relative radius to the polygon creator (use mnSelectionIndicatorThickness here, too) const double fPixelFactor(aDiscretePixel.getX() * (mnSelectionIndicatorThickness + 2.5)); const double fRelativeRadiusX(fPixelFactor / ::std::max(aDiscreteOuterRange.getWidth(), 1.0)); const double fRelativeRadiusY(fPixelFactor / ::std::max(aDiscreteOuterRange.getHeight(), 1.0)); basegfx::B2DPolyPolygon aFramePolyPolygon; const basegfx::B2DPolygon aRoundedOuterPolygon(basegfx::tools::createPolygonFromRect(aDiscreteOuterRange, fRelativeRadiusX, fRelativeRadiusY)); aFramePolyPolygon.append(aRoundedOuterPolygon); aFramePolyPolygon.append(basegfx::tools::createPolygonFromRect(aDiscreteInnerRange)); // add colored PolyPolygon const svtools::ColorConfig aColorConfig; static bool bTestWithBrightColors(false); const basegfx::BColor aFrameColor(bTestWithBrightColors ? basegfx::BColor(0,1,0) : Application::GetSettings().GetStyleSettings().GetMenuHighlightColor().getBColor()); xRetval[0] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(aFramePolyPolygon, aFrameColor)); // add aRoundedOuterPolygon again as non-filled line polygon to get the roundungs // painted correctly xRetval[1] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aRoundedOuterPolygon, aFrameColor)); return xRetval; } SdPageObjectSelectPrimitive::SdPageObjectSelectPrimitive(const basegfx::B2DRange& rRange) : SdPageObjectBasePrimitive(rRange) { } SdPageObjectSelectPrimitive::~SdPageObjectSelectPrimitive() { } ImplPrimitrive2DIDBlock(SdPageObjectSelectPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTSELECTPRIMITIVE) /////////////////////////////////////////////////////////////////////////////////////////////// // SdPageObjectPrimitive for border around bitmap visualisation class SdPageObjectBorderPrimitive : public SdPageObjectBasePrimitive { protected: // method which is to be used to implement the local decomposition of a 2D primitive. virtual drawinglayer::primitive2d::Primitive2DSequence createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const; public: // constructor and destructor SdPageObjectBorderPrimitive(const basegfx::B2DRange& rRange); ~SdPageObjectBorderPrimitive(); // provide unique ID DeclPrimitrive2DIDBlock() }; drawinglayer::primitive2d::Primitive2DSequence SdPageObjectBorderPrimitive::createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const { // since old Width/Height calculations always added a single pixel value, // it is necessary to create a inner range which is one display unit less // at the bottom right. const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); const basegfx::B2DRange aAdaptedInnerRange( getPageObjectRange().getMinX(), getPageObjectRange().getMinY(), getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY()); // Paint_Border replacement. (use aBorderColor) static bool bTestWithBrightColors(false); const svtools::ColorConfig aColorConfig; const basegfx::BColor aBorderColor(bTestWithBrightColors ? basegfx::BColor(1,0,0) : Color(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor).getBColor()); const drawinglayer::primitive2d::Primitive2DReference xReference( new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(basegfx::tools::createPolygonFromRect(aAdaptedInnerRange), aBorderColor)); return drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1); } SdPageObjectBorderPrimitive::SdPageObjectBorderPrimitive(const basegfx::B2DRange& rRange) : SdPageObjectBasePrimitive(rRange) { } SdPageObjectBorderPrimitive::~SdPageObjectBorderPrimitive() { } ImplPrimitrive2DIDBlock(SdPageObjectBorderPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTBORDERPRIMITIVE) /////////////////////////////////////////////////////////////////////////////////////////////// // SdPageObjectPrimitive for focus visualisation class SdPageObjectFocusPrimitive : public SdPageObjectBasePrimitive { private: /// Gap between border of page object and inside of focus rectangle. static const sal_Int32 mnFocusIndicatorOffset; protected: // method which is to be used to implement the local decomposition of a 2D primitive. virtual drawinglayer::primitive2d::Primitive2DSequence createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const; public: // constructor and destructor SdPageObjectFocusPrimitive(const basegfx::B2DRange& rRange); ~SdPageObjectFocusPrimitive(); // provide unique ID DeclPrimitrive2DIDBlock() }; const sal_Int32 SdPageObjectFocusPrimitive::mnFocusIndicatorOffset(2); drawinglayer::primitive2d::Primitive2DSequence SdPageObjectFocusPrimitive::createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const { drawinglayer::primitive2d::Primitive2DSequence xRetval(2); // since old Width/Height calculations always added a single pixel value, // it is necessary to create a inner range which is one display unit less // at the bottom right. const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); const basegfx::B2DRange aAdaptedInnerRange( getPageObjectRange().getMinX(), getPageObjectRange().getMinY(), getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY()); // Paint_FocusIndicator replacement. (black and white). // imitate Paint_DottedRectangle: First paint a white rectangle and above it a black dotted one basegfx::B2DRange aFocusIndicatorRange(aAdaptedInnerRange); aFocusIndicatorRange.grow(mnFocusIndicatorOffset * aDiscretePixel.getX()); // create polygon const basegfx::B2DPolygon aIndicatorPolygon(basegfx::tools::createPolygonFromRect(aFocusIndicatorRange)); // white rectangle xRetval[0] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aIndicatorPolygon, Color(COL_WHITE).getBColor())); // dotted black rectangle with same geometry ::std::vector< double > aDotDashArray; aDotDashArray.push_back(aDiscretePixel.getX()); aDotDashArray.push_back(aDiscretePixel.getX()); // prepare line and stroke attributes const drawinglayer::attribute::LineAttribute aLineAttribute(Color(COL_BLACK).getBColor()); const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDotDashArray, 2.0 * aDiscretePixel.getX()); xRetval[1] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::PolygonStrokePrimitive2D(aIndicatorPolygon, aLineAttribute, aStrokeAttribute)); return xRetval; } SdPageObjectFocusPrimitive::SdPageObjectFocusPrimitive(const basegfx::B2DRange& rRange) : SdPageObjectBasePrimitive(rRange) { } SdPageObjectFocusPrimitive::~SdPageObjectFocusPrimitive() { } ImplPrimitrive2DIDBlock(SdPageObjectFocusPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTFOCUSPRIMITIVE) /////////////////////////////////////////////////////////////////////////////////////////////// // SdPageObjectPrimitive for fade effect visualisation class SdPageObjectFadeNameNumberPrimitive : public SdPageObjectBasePrimitive { private: /// Size of width and height of the fade effect indicator in pixels. static const sal_Int32 mnFadeEffectIndicatorOffset; /// Gap between border of page object and number rectangle. static const sal_Int32 mnPageNumberOffset; /// the FadeEffect bitmap. Static since it is usable outside this primitive /// for size comparisons static BitmapEx maFadeEffectIconBitmap; /// page name, number and needed infos String maPageName; sal_uInt32 mnPageNumber; Font maPageNameFont; Size maPageNumberAreaModelSize; // bitfield unsigned mbShowFadeEffectIcon : 1; unsigned mbExcluded : 1; // private helpers const BitmapEx& getFadeEffectIconBitmap() const; protected: // method which is to be used to implement the local decomposition of a 2D primitive. virtual drawinglayer::primitive2d::Primitive2DSequence createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const; public: // constructor and destructor SdPageObjectFadeNameNumberPrimitive( const basegfx::B2DRange& rRange, const String& rPageName, sal_uInt32 nPageNumber, const Font& rPageNameFont, const Size& rPageNumberAreaModelSize, bool bShowFadeEffectIcon, bool bExcluded); ~SdPageObjectFadeNameNumberPrimitive(); // data access const String& getPageName() const { return maPageName; } sal_uInt32 getPageNumber() const { return mnPageNumber; } const Font& getPageNameFont() const { return maPageNameFont; } const Size& getPageNumberAreaModelSize() const { return maPageNumberAreaModelSize; } bool getShowFadeEffectIcon() const { return mbShowFadeEffectIcon; } bool getExcluded() const { return mbExcluded; } // compare operator virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const; // provide unique ID DeclPrimitrive2DIDBlock() }; const sal_Int32 SdPageObjectFadeNameNumberPrimitive::mnFadeEffectIndicatorOffset(9); const sal_Int32 SdPageObjectFadeNameNumberPrimitive::mnPageNumberOffset(9); BitmapEx SdPageObjectFadeNameNumberPrimitive::maFadeEffectIconBitmap; const BitmapEx& SdPageObjectFadeNameNumberPrimitive::getFadeEffectIconBitmap() const { if(maFadeEffectIconBitmap.IsEmpty()) { // prepare FadeEffectIconBitmap on demand const sal_uInt16 nIconId(Application::GetSettings().GetStyleSettings().GetHighContrastMode() ? BMP_FADE_EFFECT_INDICATOR_H : BMP_FADE_EFFECT_INDICATOR); const BitmapEx aFadeEffectIconBitmap(IconCache::Instance().GetIcon(nIconId).GetBitmapEx()); const_cast< SdPageObjectFadeNameNumberPrimitive* >(this)->maFadeEffectIconBitmap = aFadeEffectIconBitmap; } return maFadeEffectIconBitmap; } drawinglayer::primitive2d::Primitive2DSequence SdPageObjectFadeNameNumberPrimitive::createLocalDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const { const xub_StrLen nTextLength(getPageName().Len()); const sal_uInt32 nCount( (getShowFadeEffectIcon() ? 1 : 0) + // FadeEffect icon (nTextLength ? 1 : 0) + // PageName 1 + // PageNumber (always) (getExcluded() ? 2 : 0) // PageNumber crossed out ); sal_uInt32 nInsert(0); drawinglayer::primitive2d::Primitive2DSequence xRetval(nCount); // since old Width/Height calculations always added a single pixel value, // it is necessary to create a inner range which is one display unit less // at the bottom right. const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); const basegfx::B2DRange aAdaptedInnerRange( getPageObjectRange().getMinX(), getPageObjectRange().getMinY(), getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY()); // preapre TextLayouter drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; aTextLayouter.setFont(getPageNameFont()); // get font attributes ::basegfx::B2DVector aTextSizeAttribute; const drawinglayer::primitive2d::FontAttributes aFontAttributes(drawinglayer::primitive2d::getFontAttributesFromVclFont( aTextSizeAttribute, getPageNameFont(), false, false)); // prepare DXTextArray (can be empty one) const ::std::vector< double > aDXArray; // prepare locale; this may need some more information in the future const ::com::sun::star::lang::Locale aLocale; // prepare font color from System const basegfx::BColor aFontColor(Application::GetSettings().GetStyleSettings().GetFontColor().getBColor()); if(getShowFadeEffectIcon()) { // prepare fFadeEffect Sizes const basegfx::B2DVector aFadeEffectBitmapSizeLogic(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector( getFadeEffectIconBitmap().GetSizePixel().getWidth() - 1, getFadeEffectIconBitmap().GetSizePixel().getHeight() - 1)); // Paint_FadeEffectIndicator replacement. // create transformation. To avoid bitmap scaling, use bitmap size as size basegfx::B2DHomMatrix aBitmapTransform; // short form for scale and translate transformation aBitmapTransform.set(0L, 0L, aFadeEffectBitmapSizeLogic.getX()); aBitmapTransform.set(1L, 1L, aFadeEffectBitmapSizeLogic.getY()); aBitmapTransform.set(0L, 2L, aAdaptedInnerRange.getMinX()); aBitmapTransform.set(1L, 2L, aAdaptedInnerRange.getMaxY() + ((mnFadeEffectIndicatorOffset + 1) * aDiscretePixel.getX())); xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::BitmapPrimitive2D(getFadeEffectIconBitmap(), aBitmapTransform)); } if(nTextLength) { // prepare fFadeEffect Sizes since it consumes from text size const basegfx::B2DVector aFadeEffectBitmapSizeLogic(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector( getFadeEffectIconBitmap().GetSizePixel().getWidth() - 1, getFadeEffectIconBitmap().GetSizePixel().getHeight() - 1)); // Paint_PageName replacement. Get text size const double fTextWidth(aTextLayouter.getTextWidth(getPageName(), 0, nTextLength)); const double fTextHeight(getPageNameFont().GetHeight()); const double fFadeEffectWidth(aFadeEffectBitmapSizeLogic.getX() * 2.0); const double fFadeEffectTextGap(((mnFadeEffectIndicatorOffset + 2) * aDiscretePixel.getX())); String aPageName(getPageName()); // calculate text start position double fStartX( aAdaptedInnerRange.getMaxX() - fTextWidth + (aDiscretePixel.getX() * 3.0)); const double fStartY( aAdaptedInnerRange.getMaxY() + fTextHeight + fFadeEffectTextGap); const bool bNeedClipping(fStartX < aAdaptedInnerRange.getMinX() + fFadeEffectWidth); // if text is too big, clip it if(bNeedClipping) { // new left start fStartX = aAdaptedInnerRange.getMinX() + fFadeEffectWidth; // find out how many characters to use const double fAvailableLength(aAdaptedInnerRange.getWidth() - fFadeEffectWidth); static const String aThreePoints(String::CreateFromAscii("...")); const double fWidthThreePoints(aTextLayouter.getTextWidth(aThreePoints, 0, aThreePoints.Len())); xub_StrLen a(1); for(; a < (xub_StrLen)nTextLength; a++) { const double fSnippetLength(aTextLayouter.getTextWidth(aPageName, 0, a)); if(fSnippetLength + fWidthThreePoints > fAvailableLength) { break; } } // build new string aPageName = String(aPageName, 0, a - 1); aPageName += aThreePoints; } // fill text matrix ::basegfx::B2DHomMatrix aTextMatrix; aTextMatrix.set(0L, 0L, aTextSizeAttribute.getX()); aTextMatrix.set(1L, 1L, aTextSizeAttribute.getY()); aTextMatrix.set(0L, 2L, fStartX); aTextMatrix.set(1L, 2L, fStartY); // create Text primitive and add to target xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( aTextMatrix, aPageName, 0, aPageName.Len(), aDXArray, aFontAttributes, aLocale, aFontColor)); } { // Paint_PageNumber replacement. Get the range where it shall be centered and prepare the string const double fLeft(aAdaptedInnerRange.getMinX() - (mnPageNumberOffset * aDiscretePixel.getX()) - getPageNumberAreaModelSize().Width()); const double fTop(aAdaptedInnerRange.getMinY()); const basegfx::B2DRange aNumberRange(fLeft, fTop, fLeft + getPageNumberAreaModelSize().Width(), fTop + getPageNumberAreaModelSize().Height()); const String aPageNumber(String::CreateFromInt32(getPageNumber())); const xub_StrLen nNumberLen(aPageNumber.Len()); // Get text size const double fTextWidth(aTextLayouter.getTextWidth(aPageNumber, 0, nNumberLen)); const double fTextHeight(getPageNameFont().GetHeight()); // get text start postion const double fStartX(aNumberRange.getCenterX() - (fTextWidth / 2.0)); const double fStartY(aNumberRange.getMinY() + fTextHeight + aDiscretePixel.getX()); // fill text matrix ::basegfx::B2DHomMatrix aTextMatrix; aTextMatrix.set(0L, 0L, aTextSizeAttribute.getX()); aTextMatrix.set(1L, 1L, aTextSizeAttribute.getY()); aTextMatrix.set(0L, 2L, fStartX); aTextMatrix.set(1L, 2L, fStartY); // create Text primitive xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( aTextMatrix, aPageNumber, 0, nNumberLen, aDXArray, aFontAttributes, aLocale, aFontColor)); if(getExcluded()) { // create a box with strikethrough from top left to bottom right const basegfx::BColor aActiveColor(Application::GetSettings().GetStyleSettings().GetActiveColor().getBColor()); basegfx::B2DPolygon aStrikethrough; aStrikethrough.append(aNumberRange.getMinimum()); aStrikethrough.append(aNumberRange.getMaximum()); xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( basegfx::tools::createPolygonFromRect(aNumberRange), aActiveColor)); xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( aStrikethrough, aActiveColor)); } } return xRetval; } SdPageObjectFadeNameNumberPrimitive::SdPageObjectFadeNameNumberPrimitive( const basegfx::B2DRange& rRange, const String& rPageName, sal_uInt32 nPageNumber, const Font& rPageNameFont, const Size& rPageNumberAreaModelSize, bool bShowFadeEffectIcon, bool bExcluded) : SdPageObjectBasePrimitive(rRange), maPageName(rPageName), mnPageNumber(nPageNumber), maPageNameFont(rPageNameFont), maPageNumberAreaModelSize(rPageNumberAreaModelSize), mbShowFadeEffectIcon(bShowFadeEffectIcon), mbExcluded(bExcluded) { } SdPageObjectFadeNameNumberPrimitive::~SdPageObjectFadeNameNumberPrimitive() { } bool SdPageObjectFadeNameNumberPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const { if(SdPageObjectBasePrimitive::operator==(rPrimitive)) { const SdPageObjectFadeNameNumberPrimitive& rCompare = static_cast< const SdPageObjectFadeNameNumberPrimitive& >(rPrimitive); return (getPageName() == rCompare.getPageName() && getPageNumber() == rCompare.getPageNumber() && getPageNameFont() == rCompare.getPageNameFont() && getPageNumberAreaModelSize() == rCompare.getPageNumberAreaModelSize() && getShowFadeEffectIcon() == rCompare.getShowFadeEffectIcon() && getExcluded() == rCompare.getExcluded()); } return false; } ImplPrimitrive2DIDBlock(SdPageObjectFadeNameNumberPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTFADENAMENUMBERPRIMITIVE) /////////////////////////////////////////////////////////////////////////////////////////////// // createPrimitive2DSequence // // This method will replace the whole painting mechanism. Task is no longer to paint stuff to an OutDev, // but to provide the necessary geometrical information using primitives. drawinglayer::primitive2d::Primitive2DSequence PageObjectViewObjectContact::createPrimitive2DSequence(const sdr::contact::DisplayInfo& rDisplayInfo) const { // OutputDevice* pDevice = rDisplayInfo.GetDIOutputDevice(); OutputDevice* pDevice = GetObjectContact().TryToGetOutputDevice(); // get primitive vector from parent class. Do remember the contents for later use; this // is done to create the page content renderer (see PagePrimitiveExtractor in svx) at the // original object and to setup the draw hierarchy there so that changes to VCs of displayed // objects will lead to InvalidatePartOfView-calls which will be forwarded from the helper-OC // to this VOC in calling a ActionChanged(). // // This already produces the displayable page content as a primitive sequence, complete with // embedding in the page visualizer, clipping if needed and object and aspect ratio // preparations. It would thus be the base for creating the cached visualisation, too, // by just painting extactly this primitive sequence. // // Currently, this slows down PagePane display heavily. Reason is that the current mechanism // to react on a SdrObject change in an edit view is to react on the ModelChange and to completely // reset the PagePane (delete SdrPageObjs, re-create and layout them). This works, but kicks // the complete sequence of primitive creation at VOCs and VCs and their buffering out of // memory each time. So there are two choices: // // 1, disable getting the sequence of primtives // -> invalidate uses ModelChange // -> cache repaint uses complete view creation and repainting // // 2, create and use the sequence of primitives // -> invalidate would not need ModelChange, no destroy/recreate of SdrObjects, no rearrange, // the invalidate and the following repaint would exactly update the SdrPages involved and // use the DrawingLayer provided ActionChanged() invalidations over the VOCs and VCs // -> cache repaint could use the here offered sequence of primitives to re-create the bitmap // (just hand over the local member to the cache) // // For the moment i will use (1) and disable primitive creation for SdrPageObj contents here // const_cast< PageObjectViewObjectContact* >(this)->mxCurrentPageContents = ViewObjectContactOfPageObj::createPrimitive2DSequence(rDisplayInfo); // assert when this call is issued indirectly from the destructor of // this instance. This is not allowed and needs to be looked at #ifdef DBG_UTIL if(mbInDestructor) { OSL_ENSURE(false, "Higher call inside PageObjectViewObjectContact in destructor (!)"); } #endif // Check if buffering can and shall be done. if (pDevice != NULL && !GetObjectContact().isOutputToPrinter() && !GetObjectContact().isOutputToRecordingMetaFile() && !mbInDestructor) { // get inner and outer logic rectangles. Use model data directly for creation. Do NOT use getBoundRect()/ // getSnapRect() functionality; these will use the sequence of primitives in the long run itself. SdrPageObj // is a SdrObject, so use SdrObject::aOutRect as model data. Access using GetLastBoundRect() to not execute anything PageObjectViewContact& rPaObVOC(static_cast< PageObjectViewContact& >(GetViewContact())); const Rectangle aInnerLogic(rPaObVOC.GetPageObject().GetLastBoundRect()); // get BitmapEx from cache. Do exactly the same as Paint_Preview() to avoid a repaint loop // caused by slightly different pixel sizes of what the cache sees as pixel size and what is // calculated here in discrete coordinates. This includes to not use LogicToPiyel on the Rectangle, // but to do the same as the GetBoundingBox() implementation const Rectangle aInnerPixel(Rectangle(pDevice->LogicToPixel(aInnerLogic.TopLeft()), pDevice->LogicToPixel(aInnerLogic.GetSize()))); BitmapEx aBitmapEx(const_cast< PageObjectViewObjectContact* >(this)->GetPreview(rDisplayInfo, aInnerPixel)); // prepare inner range const basegfx::B2DRange aInnerRange(aInnerLogic.Left(), aInnerLogic.Top(), aInnerLogic.Right(), aInnerLogic.Bottom()); // provide default parameters String aPageName; Font aPageNameFont; sal_uInt32 nPageNumber(0); Size aPageNumberAreaModelSize; bool bShowFadeEffectIcon(false); bool bExcluded(false); if(GetPage()) { const SdPage* pPage = static_cast(GetPage()); // decide if fade effect indicator will be painted if(pPage->getTransitionType() > 0) { bShowFadeEffectIcon = true; } // prepare PageName, PageNumber, font and AreaModelSize aPageName = pPage->GetName(); aPageNameFont = *FontProvider::Instance().GetFont(*pDevice); nPageNumber = ((pPage->GetPageNum() - 1) / 2) + 1; aPageNumberAreaModelSize = GetPageDescriptor()->GetPageNumberAreaModelSize(); if(!aPageName.Len()) { aPageName = String(SdResId(STR_PAGE)); aPageName += String::CreateFromInt32(nPageNumber); } // decide if page is excluded bExcluded = pPage->IsExcluded(); } // create specialized primitives for focus, select and PagePreview itself const bool bCreateBitmap(!aBitmapEx.IsEmpty()); const bool bCreateFocused(GetPageDescriptor()->IsFocused()); const bool bCreateSelected(GetPageDescriptor()->IsSelected()); const sal_uInt32 nCount( (bCreateBitmap ? 1 : 0) + // bitmap itself 1 + // border around bitmap (always) 1 + // FadeEffect, PageName and PageNumber visualisation (always) (bCreateFocused ? 1 : 0) + // create focused (bCreateSelected ? 1 : 0) // create selected ); sal_uInt32 nInsert(0); drawinglayer::primitive2d::Primitive2DSequence xRetval(nCount); if(bCreateBitmap) { // add selection indicator if used xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new SdPageObjectPageBitmapPrimitive(aInnerRange, aBitmapEx)); } if(true) { // add border (always) xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new SdPageObjectBorderPrimitive(aInnerRange)); } if(true) { // add fade effext, page name and number if used xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new SdPageObjectFadeNameNumberPrimitive( aInnerRange, aPageName, nPageNumber, aPageNameFont, aPageNumberAreaModelSize, bShowFadeEffectIcon, bExcluded)); } if(bCreateSelected) { // add selection indicator if used xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new SdPageObjectSelectPrimitive(aInnerRange)); } if(bCreateFocused) { // add focus indicator if used xRetval[nInsert++] = drawinglayer::primitive2d::Primitive2DReference(new SdPageObjectFocusPrimitive(aInnerRange)); } return xRetval; } else { // Call parent. Output to printer or metafile will use vector data, not cached bitmaps return ViewObjectContactOfPageObj::createPrimitive2DSequence(rDisplayInfo); } } BitmapEx PageObjectViewObjectContact::CreatePreview (const DisplayInfo& /*rDisplayInfo*/) { const SdPage* pPage = static_cast(GetPage()); OutputDevice* pDevice = GetObjectContact().TryToGetOutputDevice(); if(pDevice) { Rectangle aPreviewPixelBox (GetBoundingBox(*pDevice,PreviewBoundingBox,PixelCoordinateSystem)); PreviewRenderer aRenderer (pDevice); Image aPreview (aRenderer.RenderPage( pPage, aPreviewPixelBox.GetSize(), String())); return aPreview.GetBitmapEx(); } else { return BitmapEx(); } } BitmapEx PageObjectViewObjectContact::GetPreview ( const DisplayInfo& rDisplayInfo, const Rectangle& rNewSizePixel) { BitmapEx aBitmap; try { // assert when this call is issued indirectly from the destructor of // this instance. This is not allowed and needs to be looked at OSL_ENSURE(!mbInDestructor, "Higher call inside PageObjectViewObjectContact in destructor (!)"); if (!mbInDestructor) { if (mpCache != NULL) { aBitmap = mpCache->GetPreviewBitmap( GetPage(), rNewSizePixel.GetSize()); mpCache->SetPreciousFlag(GetPage(), true); } else aBitmap = CreatePreview(rDisplayInfo); } } catch (const ::com::sun::star::uno::Exception&) { OSL_TRACE("PageObjectViewObjectContact::GetPreview: caught exception"); } return aBitmap; } const SdrPage* PageObjectViewObjectContact::GetPage (void) const { return static_cast(GetViewContact()).GetPage(); } void PageObjectViewObjectContact::ActionChanged (void) { // Even when we are called from destructor we still have to invalide // the preview bitmap in the cache. const SdrPage* pPage = GetPage(); SdDrawDocument* pDocument = dynamic_cast(pPage->GetModel()); if (mpCache!=NULL && pPage!=NULL && pDocument!=NULL) { cache::PageCacheManager::Instance()->InvalidatePreviewBitmap( pDocument->getUnoModel(), GetPage()); } mbIsBackgroundColorUpdatePending = true; // call parent ViewObjectContactOfPageObj::ActionChanged(); } void PageObjectViewObjectContact::PaintMouseOverEffect ( OutputDevice& rDevice, bool bVisible) const { // When the selection frame is painted the mouse over frame is not // visible and does not have to be painted. if (GetPageDescriptor()->IsSelected()) if (mpProperties.get()!=NULL && mpProperties->IsShowSelection()) return; ULONG nPreviousDrawMode = rDevice.GetDrawMode(); rDevice.SetDrawMode (DRAWMODE_DEFAULT); Rectangle aInner (GetBoundingBox(rDevice,PreviewBoundingBox,PixelCoordinateSystem)); rDevice.EnableMapMode (FALSE); Color aSelectionColor (GetColor(rDevice, CS_SELECTION)); Color aBackgroundColor (GetColor(rDevice, CS_BACKGROUND)); Color aFrameColor (bVisible ? aSelectionColor : aBackgroundColor); Color aCornerColor (aBackgroundColor); rDevice.SetFillColor (); rDevice.SetLineColor (aFrameColor); // Paint the frame. for (int nOffset=mnMouseOverEffectOffset; nOffsetPixelToLogic (Size ( mnPageNumberOffset+1, mnSelectionIndicatorOffset + mnSelectionIndicatorThickness))); Size aBottomRightBorders (pDevice->PixelToLogic (Size ( mnSelectionIndicatorOffset + mnSelectionIndicatorThickness, mnFadeEffectIndicatorOffset))); aModelBorder = SvBorder ( aTopLeftBorders.Width(), aTopLeftBorders.Height(), aBottomRightBorders.Width(), aBottomRightBorders.Height()); // 2. Add the device dependent values. // Calculate the area of the page number. Size aPageNumberModelSize ( CalculatePageNumberAreaModelSize (pDevice, nPageCount)); // Update the border. aModelBorder.Left() += aPageNumberModelSize.Width(); // The height of the page number area is the same as the height of // the page name area. aModelBorder.Bottom() += aPageNumberModelSize.Height(); } return aModelBorder; } Size PageObjectViewObjectContact::CalculatePageNumberAreaModelSize ( OutputDevice* pDevice, int nPageCount) { // Set the correct font. Font aOriginalFont (pDevice->GetFont()); pDevice->SetFont(*FontProvider::Instance().GetFont(*pDevice)); String sPageNumberTemplate; if (nPageCount < 10) sPageNumberTemplate = String::CreateFromAscii("9"); else if (nPageCount < 100) sPageNumberTemplate = String::CreateFromAscii("99"); else if (nPageCount < 200) // Just for the case that 1 is narrower than 9. sPageNumberTemplate = String::CreateFromAscii("199"); else if (nPageCount < 1000) sPageNumberTemplate = String::CreateFromAscii("999"); else sPageNumberTemplate = String::CreateFromAscii("9999"); // More then 9999 pages are not handled. Size aSize ( pDevice->GetTextWidth (sPageNumberTemplate), pDevice->GetTextHeight ()); pDevice->SetFont (aOriginalFont); return aSize; } model::SharedPageDescriptor PageObjectViewObjectContact::GetPageDescriptor (void) const { PageObjectViewContact& rViewContact ( static_cast(GetViewContact())); PageObject& rPageObject ( static_cast(rViewContact.GetPageObject())); return rPageObject.GetDescriptor(); } Color PageObjectViewObjectContact::GetColor ( const OutputDevice& rDevice, const ColorSpec eSpec, const double nOpacity) const { (void)rDevice; if (mbIsBackgroundColorUpdatePending) { mbIsBackgroundColorUpdatePending = false; maBackgroundColor = mpProperties->GetBackgroundColor(); } Color aColor; switch (eSpec) { case CS_SELECTION: aColor = mpProperties->GetSelectionColor(); break; case CS_BACKGROUND: if (mpProperties.get()!=NULL && mpProperties->IsHighlightCurrentSlide() && GetPageDescriptor()->IsCurrentPage()) { aColor = mpProperties->GetHighlightColor(); } else aColor = maBackgroundColor; break; case CS_WINDOW: aColor = maBackgroundColor; break; case CS_TEXT: default: aColor = mpProperties->GetTextColor(); break; } aColor.Merge(maBackgroundColor, BYTE(255*(nOpacity) + 0.5)); return aColor; } } } } // end of namespace ::sd::slidesorter::view