/* -*- 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 "gradtrns.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // #i15222# // Due to the resource problems in Win95/98 with bitmap resources I // will change this handle bitmap providing class. Old version was splitting // and preparing all small handle bitmaps in device bitmap format, now this will // be done on the fly. Thus, there is only one big bitmap in memory. With // three source bitmaps, this will be 3 system bitmap resources instead of hundreds. // The price for that needs to be evaluated. Maybe we will need another change here // if this is too expensive. class SdrHdlBitmapSet { // the bitmap holding all information BitmapEx maMarkersBitmap; // the cropped Bitmaps for reusage ::std::vector< BitmapEx > maRealMarkers; // helpers BitmapEx& impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle); public: explicit SdrHdlBitmapSet(); const BitmapEx& GetBitmapEx(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd); }; } #define KIND_COUNT (14) #define INDEX_COUNT (6) #define INDIVIDUAL_COUNT (5) SdrHdlBitmapSet::SdrHdlBitmapSet() : maMarkersBitmap(SIP_SA_MARKERS), // 15 kinds (BitmapMarkerKind) use index [0..5] + 5 extra maRealMarkers((KIND_COUNT * INDEX_COUNT) + INDIVIDUAL_COUNT) { } BitmapEx& SdrHdlBitmapSet::impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle) { BitmapEx& rTargetBitmap = maRealMarkers[nIndex]; if(rTargetBitmap.IsEmpty()) { rTargetBitmap = maMarkersBitmap; rTargetBitmap.Crop(rRectangle); } return rTargetBitmap; } // change getting of bitmap to use the big resource bitmap const BitmapEx& SdrHdlBitmapSet::GetBitmapEx(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd) { // fill in size and source position in maMarkersBitmap const sal_uInt16 nYPos(nInd * 11); switch(eKindOfMarker) { default: { OSL_FAIL( "Unknown kind of marker." ); [[fallthrough]]; // return Rect_9x9 as default } case BitmapMarkerKind::Rect_9x9: { return impGetOrCreateTargetBitmap((1 * INDEX_COUNT) + nInd, tools::Rectangle(Point(7, nYPos), Size(9, 9))); } case BitmapMarkerKind::Rect_7x7: { return impGetOrCreateTargetBitmap((0 * INDEX_COUNT) + nInd, tools::Rectangle(Point(0, nYPos), Size(7, 7))); } case BitmapMarkerKind::Rect_11x11: { return impGetOrCreateTargetBitmap((2 * INDEX_COUNT) + nInd, tools::Rectangle(Point(16, nYPos), Size(11, 11))); } case BitmapMarkerKind::Rect_13x13: { const sal_uInt16 nIndex((3 * INDEX_COUNT) + nInd); switch(nInd) { case 0: { return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 66), Size(13, 13))); } case 1: { return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 66), Size(13, 13))); } case 2: { return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 79), Size(13, 13))); } case 3: { return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 79), Size(13, 13))); } case 4: { return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 79), Size(13, 13))); } default: // case 5: { return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 66), Size(13, 13))); } } } case BitmapMarkerKind::Circ_7x7: case BitmapMarkerKind::Customshape_7x7: { return impGetOrCreateTargetBitmap((4 * INDEX_COUNT) + nInd, tools::Rectangle(Point(27, nYPos), Size(7, 7))); } case BitmapMarkerKind::Circ_9x9: case BitmapMarkerKind::Customshape_9x9: { return impGetOrCreateTargetBitmap((5 * INDEX_COUNT) + nInd, tools::Rectangle(Point(34, nYPos), Size(9, 9))); } case BitmapMarkerKind::Circ_11x11: case BitmapMarkerKind::Customshape_11x11: { return impGetOrCreateTargetBitmap((6 * INDEX_COUNT) + nInd, tools::Rectangle(Point(43, nYPos), Size(11, 11))); } case BitmapMarkerKind::Elli_7x9: { return impGetOrCreateTargetBitmap((7 * INDEX_COUNT) + nInd, tools::Rectangle(Point(54, nYPos), Size(7, 9))); } case BitmapMarkerKind::Elli_9x11: { return impGetOrCreateTargetBitmap((8 * INDEX_COUNT) + nInd, tools::Rectangle(Point(61, nYPos), Size(9, 11))); } case BitmapMarkerKind::Elli_9x7: { return impGetOrCreateTargetBitmap((9 * INDEX_COUNT) + nInd, tools::Rectangle(Point(70, nYPos), Size(9, 7))); } case BitmapMarkerKind::Elli_11x9: { return impGetOrCreateTargetBitmap((10 * INDEX_COUNT) + nInd, tools::Rectangle(Point(79, nYPos), Size(11, 9))); } case BitmapMarkerKind::RectPlus_7x7: { return impGetOrCreateTargetBitmap((11 * INDEX_COUNT) + nInd, tools::Rectangle(Point(90, nYPos), Size(7, 7))); } case BitmapMarkerKind::RectPlus_9x9: { return impGetOrCreateTargetBitmap((12 * INDEX_COUNT) + nInd, tools::Rectangle(Point(97, nYPos), Size(9, 9))); } case BitmapMarkerKind::RectPlus_11x11: { return impGetOrCreateTargetBitmap((13 * INDEX_COUNT) + nInd, tools::Rectangle(Point(106, nYPos), Size(11, 11))); } case BitmapMarkerKind::Crosshair: { return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 0, tools::Rectangle(Point(0, 68), Size(15, 15))); } case BitmapMarkerKind::Glue: { return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 1, tools::Rectangle(Point(15, 76), Size(9, 9))); } case BitmapMarkerKind::Glue_Deselected: { return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 2, tools::Rectangle(Point(15, 67), Size(9, 9))); } case BitmapMarkerKind::Anchor: // AnchorTR for SW case BitmapMarkerKind::AnchorTR: { return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 3, tools::Rectangle(Point(24, 67), Size(24, 24))); } // add AnchorPressed to be able to animate anchor control case BitmapMarkerKind::AnchorPressed: case BitmapMarkerKind::AnchorPressedTR: { return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 4, tools::Rectangle(Point(48, 67), Size(24, 24))); } } } SdrHdl::SdrHdl(): m_pObj(nullptr), m_pPV(nullptr), m_pHdlList(nullptr), m_eKind(SdrHdlKind::Move), m_nRotationAngle(0), m_nObjHdlNum(0), m_nPolyNum(0), m_nPPntNum(0), m_nSourceHdlNum(0), m_bSelect(false), m_b1PixMore(false), m_bPlusHdl(false), mbMoveOutside(false), mbMouseOver(false) { } SdrHdl::SdrHdl(const Point& rPnt, SdrHdlKind eNewKind): m_pObj(nullptr), m_pPV(nullptr), m_pHdlList(nullptr), m_aPos(rPnt), m_eKind(eNewKind), m_nRotationAngle(0), m_nObjHdlNum(0), m_nPolyNum(0), m_nPPntNum(0), m_nSourceHdlNum(0), m_bSelect(false), m_b1PixMore(false), m_bPlusHdl(false), mbMoveOutside(false), mbMouseOver(false) { } SdrHdl::~SdrHdl() { GetRidOfIAObject(); } void SdrHdl::Set1PixMore(bool bJa) { if(m_b1PixMore != bJa) { m_b1PixMore = bJa; // create new display Touch(); } } void SdrHdl::SetMoveOutside( bool bMoveOutside ) { if(mbMoveOutside != bMoveOutside) { mbMoveOutside = bMoveOutside; // create new display Touch(); } } void SdrHdl::SetRotationAngle(Degree100 n) { if(m_nRotationAngle != n) { m_nRotationAngle = n; // create new display Touch(); } } void SdrHdl::SetPos(const Point& rPnt) { if(m_aPos != rPnt) { // remember new position m_aPos = rPnt; // create new display Touch(); } } void SdrHdl::SetSelected(bool bJa) { if(m_bSelect != bJa) { // remember new value m_bSelect = bJa; // create new display Touch(); } } void SdrHdl::SetHdlList(SdrHdlList* pList) { if(m_pHdlList != pList) { // remember list m_pHdlList = pList; // now it's possible to create graphic representation Touch(); } } void SdrHdl::SetObj(SdrObject* pNewObj) { if(m_pObj != pNewObj) { // remember new object m_pObj = pNewObj; // graphic representation may have changed Touch(); } } void SdrHdl::Touch() { // force update of graphic representation CreateB2dIAObject(); } void SdrHdl::GetRidOfIAObject() { // OVERLAYMANAGER maOverlayGroup.clear(); } void SdrHdl::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if(!m_pHdlList || !m_pHdlList->GetView() || m_pHdlList->GetView()->areMarkHandlesHidden()) return; BitmapColorIndex eColIndex = BitmapColorIndex::LightGreen; BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7; bool bRot = m_pHdlList->IsRotateShear(); if(m_pObj) eColIndex = m_bSelect ? BitmapColorIndex::Cyan : BitmapColorIndex::LightCyan; if(bRot) { // red rotation handles if(m_pObj && m_bSelect) eColIndex = BitmapColorIndex::Red; else eColIndex = BitmapColorIndex::LightRed; } switch(m_eKind) { case SdrHdlKind::Move: { eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7; break; } case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight: case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight: { // corner handles if(bRot) { eKindOfMarker = BitmapMarkerKind::Circ_7x7; } else { eKindOfMarker = BitmapMarkerKind::Rect_7x7; } break; } case SdrHdlKind::Upper: case SdrHdlKind::Lower: { // Upper/Lower handles if(bRot) { eKindOfMarker = BitmapMarkerKind::Elli_9x7; } else { eKindOfMarker = BitmapMarkerKind::Rect_7x7; } break; } case SdrHdlKind::Left: case SdrHdlKind::Right: { // Left/Right handles if(bRot) { eKindOfMarker = BitmapMarkerKind::Elli_7x9; } else { eKindOfMarker = BitmapMarkerKind::Rect_7x7; } break; } case SdrHdlKind::Poly: { if(bRot) { eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Circ_9x9 : BitmapMarkerKind::Circ_7x7; } else { eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7; } break; } case SdrHdlKind::BezierWeight: // weight at poly { eKindOfMarker = BitmapMarkerKind::Circ_7x7; break; } case SdrHdlKind::Circle: { eKindOfMarker = BitmapMarkerKind::Rect_11x11; break; } case SdrHdlKind::Ref1: case SdrHdlKind::Ref2: { eKindOfMarker = BitmapMarkerKind::Crosshair; break; } case SdrHdlKind::Glue: { eKindOfMarker = BitmapMarkerKind::Glue; break; } case SdrHdlKind::Anchor: { eKindOfMarker = BitmapMarkerKind::Anchor; break; } case SdrHdlKind::User: { break; } // top right anchor for SW case SdrHdlKind::Anchor_TR: { eKindOfMarker = BitmapMarkerKind::AnchorTR; break; } // for SJ and the CustomShapeHandles: case SdrHdlKind::CustomShape1: { eKindOfMarker = m_b1PixMore ? BitmapMarkerKind::Customshape_9x9 : BitmapMarkerKind::Customshape_7x7; eColIndex = BitmapColorIndex::Yellow; break; } default: break; } SdrMarkView* pView = m_pHdlList->GetView(); SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b]; const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { Point aMoveOutsideOffset(0, 0); OutputDevice& rOutDev = rPageWindow.GetPaintWindow().GetOutputDevice(); // add offset if necessary if(m_pHdlList->IsMoveOutside() || mbMoveOutside) { Size aOffset = rOutDev.PixelToLogic(Size(4, 4)); if(m_eKind == SdrHdlKind::UpperLeft || m_eKind == SdrHdlKind::Upper || m_eKind == SdrHdlKind::UpperRight) aMoveOutsideOffset.AdjustY( -(aOffset.Width()) ); if(m_eKind == SdrHdlKind::LowerLeft || m_eKind == SdrHdlKind::Lower || m_eKind == SdrHdlKind::LowerRight) aMoveOutsideOffset.AdjustY(aOffset.Height() ); if(m_eKind == SdrHdlKind::UpperLeft || m_eKind == SdrHdlKind::Left || m_eKind == SdrHdlKind::LowerLeft) aMoveOutsideOffset.AdjustX( -(aOffset.Width()) ); if(m_eKind == SdrHdlKind::UpperRight || m_eKind == SdrHdlKind::Right || m_eKind == SdrHdlKind::LowerRight) aMoveOutsideOffset.AdjustX(aOffset.Height() ); } const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y()); std::unique_ptr pNewOverlayObject; if (getenv ("SVX_DRAW_HANDLES") && (eKindOfMarker == BitmapMarkerKind::Rect_7x7 || eKindOfMarker == BitmapMarkerKind::Rect_9x9 || eKindOfMarker == BitmapMarkerKind::Rect_11x11)) { double fSize = 7.0; switch (eKindOfMarker) { case BitmapMarkerKind::Rect_9x9: fSize = 9.0; break; case BitmapMarkerKind::Rect_11x11: fSize = 11.0; break; default: break; } float fScalingFactor = rOutDev.GetDPIScaleFactor(); basegfx::B2DSize aB2DSize(fSize * fScalingFactor, fSize * fScalingFactor); Color aHandleFillColor(COL_LIGHTGREEN); switch (eColIndex) { case BitmapColorIndex::Cyan: aHandleFillColor = COL_CYAN; break; case BitmapColorIndex::LightCyan: aHandleFillColor = COL_LIGHTCYAN; break; case BitmapColorIndex::Red: aHandleFillColor = COL_RED; break; case BitmapColorIndex::LightRed: aHandleFillColor = COL_LIGHTRED; break; case BitmapColorIndex::Yellow: aHandleFillColor = COL_YELLOW; break; default: break; } pNewOverlayObject.reset(new sdr::overlay::OverlayHandle(aPosition, aB2DSize, /*HandleStrokeColor*/COL_BLACK, aHandleFillColor)); } else { pNewOverlayObject = CreateOverlayObject( aPosition, eColIndex, eKindOfMarker, aMoveOutsideOffset); } // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } BitmapMarkerKind SdrHdl::GetNextBigger(BitmapMarkerKind eKnd) { BitmapMarkerKind eRetval(eKnd); switch(eKnd) { case BitmapMarkerKind::Rect_7x7: eRetval = BitmapMarkerKind::Rect_9x9; break; case BitmapMarkerKind::Rect_9x9: eRetval = BitmapMarkerKind::Rect_11x11; break; case BitmapMarkerKind::Rect_11x11: eRetval = BitmapMarkerKind::Rect_13x13; break; case BitmapMarkerKind::Circ_7x7: eRetval = BitmapMarkerKind::Circ_9x9; break; case BitmapMarkerKind::Circ_9x9: eRetval = BitmapMarkerKind::Circ_11x11; break; case BitmapMarkerKind::Customshape_7x7: eRetval = BitmapMarkerKind::Customshape_9x9; break; case BitmapMarkerKind::Customshape_9x9: eRetval = BitmapMarkerKind::Customshape_11x11; break; //case BitmapMarkerKind::Customshape_11x11: eRetval = ; break; case BitmapMarkerKind::Elli_7x9: eRetval = BitmapMarkerKind::Elli_9x11; break; case BitmapMarkerKind::Elli_9x7: eRetval = BitmapMarkerKind::Elli_11x9; break; case BitmapMarkerKind::RectPlus_7x7: eRetval = BitmapMarkerKind::RectPlus_9x9; break; case BitmapMarkerKind::RectPlus_9x9: eRetval = BitmapMarkerKind::RectPlus_11x11; break; // let anchor blink with its pressed state case BitmapMarkerKind::Anchor: eRetval = BitmapMarkerKind::AnchorPressed; break; // same for AnchorTR case BitmapMarkerKind::AnchorTR: eRetval = BitmapMarkerKind::AnchorPressedTR; break; default: break; } return eRetval; } namespace { OUString appendMarkerName(BitmapMarkerKind eKindOfMarker) { switch(eKindOfMarker) { case BitmapMarkerKind::Rect_7x7: return u"rect7"_ustr; case BitmapMarkerKind::Rect_9x9: return u"rect9"_ustr; case BitmapMarkerKind::Rect_11x11: return u"rect11"_ustr; case BitmapMarkerKind::Rect_13x13: return u"rect13"_ustr; case BitmapMarkerKind::Circ_7x7: case BitmapMarkerKind::Customshape_7x7: return u"circ7"_ustr; case BitmapMarkerKind::Circ_9x9: case BitmapMarkerKind::Customshape_9x9: return u"circ9"_ustr; case BitmapMarkerKind::Circ_11x11: case BitmapMarkerKind::Customshape_11x11: return u"circ11"_ustr; case BitmapMarkerKind::Elli_7x9: return u"elli7x9"_ustr; case BitmapMarkerKind::Elli_9x11: return u"elli9x11"_ustr; case BitmapMarkerKind::Elli_9x7: return u"elli9x7"_ustr; case BitmapMarkerKind::Elli_11x9: return u"elli11x9"_ustr; case BitmapMarkerKind::RectPlus_7x7: return u"rectplus7"_ustr; case BitmapMarkerKind::RectPlus_9x9: return u"rectplus9"_ustr; case BitmapMarkerKind::RectPlus_11x11: return u"rectplus11"_ustr; case BitmapMarkerKind::Crosshair: return u"cross"_ustr; case BitmapMarkerKind::Anchor: case BitmapMarkerKind::AnchorTR: return u"anchor"_ustr; case BitmapMarkerKind::AnchorPressed: case BitmapMarkerKind::AnchorPressedTR: return u"anchor-pressed"_ustr; case BitmapMarkerKind::Glue: return u"glue-selected"_ustr; case BitmapMarkerKind::Glue_Deselected: return u"glue-unselected"_ustr; default: break; } return OUString(); } OUString appendMarkerColor(BitmapColorIndex eIndex) { switch(eIndex) { case BitmapColorIndex::LightGreen: return u"1"_ustr; case BitmapColorIndex::Cyan: return u"2"_ustr; case BitmapColorIndex::LightCyan: return u"3"_ustr; case BitmapColorIndex::Red: return u"4"_ustr; case BitmapColorIndex::LightRed: return u"5"_ustr; case BitmapColorIndex::Yellow: return u"6"_ustr; default: break; } return OUString(); } BitmapEx ImpGetBitmapEx(BitmapMarkerKind eKindOfMarker, BitmapColorIndex eIndex) { // use this code path only when we use HiDPI (for now) if (Application::GetDefaultDevice()->GetDPIScalePercentage() > 100) { OUString sMarkerName = appendMarkerName(eKindOfMarker); if (!sMarkerName.isEmpty()) { OUString sMarkerPrefix(u"svx/res/marker-"_ustr); BitmapEx aBitmapEx; if (eKindOfMarker == BitmapMarkerKind::Crosshair || eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressed || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR || eKindOfMarker == BitmapMarkerKind::Glue || eKindOfMarker == BitmapMarkerKind::Glue_Deselected) { aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + ".png"); } else { aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + "-" + appendMarkerColor(eIndex) + ".png"); } if (!aBitmapEx.IsEmpty()) return aBitmapEx; } } // if we can't load the marker... static tools::DeleteOnDeinit< SdrHdlBitmapSet > aModernSet {}; return aModernSet.get()->GetBitmapEx(eKindOfMarker, sal_uInt16(eIndex)); } } // end anonymous namespace std::unique_ptr SdrHdl::CreateOverlayObject( const basegfx::B2DPoint& rPos, BitmapColorIndex eColIndex, BitmapMarkerKind eKindOfMarker, Point aMoveOutsideOffset) { std::unique_ptr pRetval; // support bigger sizes bool bForceBiggerSize(false); if (m_pHdlList && m_pHdlList->GetHdlSize() > 3) { switch(eKindOfMarker) { case BitmapMarkerKind::Anchor: case BitmapMarkerKind::AnchorPressed: case BitmapMarkerKind::AnchorTR: case BitmapMarkerKind::AnchorPressedTR: { // #i121463# For anchor, do not simply make bigger because of HdlSize, // do it dependent of IsSelected() which Writer can set in drag mode if(IsSelected()) { bForceBiggerSize = true; } break; } default: { bForceBiggerSize = true; break; } } } if(bForceBiggerSize) { eKindOfMarker = GetNextBigger(eKindOfMarker); } // This handle has the focus, visualize it if(IsFocusHdl() && m_pHdlList && m_pHdlList->GetFocusHdl() == this) { // create animated handle BitmapMarkerKind eNextBigger = GetNextBigger(eKindOfMarker); if(eNextBigger == eKindOfMarker) { // this may happen for the not supported getting-bigger types. // Choose an alternative here switch(eKindOfMarker) { case BitmapMarkerKind::Rect_13x13: eNextBigger = BitmapMarkerKind::Rect_11x11; break; case BitmapMarkerKind::Circ_11x11: eNextBigger = BitmapMarkerKind::Elli_11x9; break; case BitmapMarkerKind::Elli_9x11: eNextBigger = BitmapMarkerKind::Elli_11x9; break; case BitmapMarkerKind::Elli_11x9: eNextBigger = BitmapMarkerKind::Elli_9x11; break; case BitmapMarkerKind::RectPlus_11x11: eNextBigger = BitmapMarkerKind::Rect_13x13; break; case BitmapMarkerKind::Glue: eNextBigger = BitmapMarkerKind::Crosshair; break; case BitmapMarkerKind::Crosshair: case BitmapMarkerKind::Glue_Deselected: eNextBigger = BitmapMarkerKind::Glue; break; default: break; } } // create animated handle BitmapEx aBmpEx1 = ImpGetBitmapEx(eKindOfMarker, eColIndex); BitmapEx aBmpEx2 = ImpGetBitmapEx(eNextBigger, eColIndex); // #i53216# Use system cursor blink time. Use the unsigned value. const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); const sal_uInt64 nBlinkTime(rStyleSettings.GetCursorBlinkTime()); if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed) { // when anchor is used take upper left as reference point inside the handle pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime)); } else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR) { // AnchorTR for SW, take top right as (0,0) pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime, static_cast(aBmpEx1.GetSizePixel().Width() - 1), 0, static_cast(aBmpEx2.GetSizePixel().Width() - 1), 0)); } else { // create centered handle as default pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime, static_cast(aBmpEx1.GetSizePixel().Width() - 1) >> 1, static_cast(aBmpEx1.GetSizePixel().Height() - 1) >> 1, static_cast(aBmpEx2.GetSizePixel().Width() - 1) >> 1, static_cast(aBmpEx2.GetSizePixel().Height() - 1) >> 1)); } } else { // create normal handle: use ImpGetBitmapEx(...) now BitmapEx aBmpEx = ImpGetBitmapEx(eKindOfMarker, eColIndex); // When the image with handles is not found, the bitmap returned is // empty. This is a problem when we use LibreOffice as a library // (through LOKit - for example on Android) even when we don't show // the handles, because the hit test would always return false. // // This HACK replaces the empty bitmap with a black 13x13 bitmap handle // so that the hit test works for this case. if (aBmpEx.IsEmpty()) { aBmpEx = BitmapEx(Size(13, 13), vcl::PixelFormat::N24_BPP); aBmpEx.Erase(COL_BLACK); } if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed) { // upper left as reference point inside the handle for AnchorPressed, too pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx)); } else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR) { // AnchorTR for SW, take top right as (0,0) pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx, static_cast(aBmpEx.GetSizePixel().Width() - 1), 0)); } else { sal_uInt16 nCenX(static_cast(aBmpEx.GetSizePixel().Width() - 1) >> 1); sal_uInt16 nCenY(static_cast(aBmpEx.GetSizePixel().Height() - 1) >> 1); if(aMoveOutsideOffset.X() > 0) { nCenX = 0; } else if(aMoveOutsideOffset.X() < 0) { nCenX = static_cast(aBmpEx.GetSizePixel().Width() - 1); } if(aMoveOutsideOffset.Y() > 0) { nCenY = 0; } else if(aMoveOutsideOffset.Y() < 0) { nCenY = static_cast(aBmpEx.GetSizePixel().Height() - 1); } // create centered handle as default pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx, nCenX, nCenY)); } } return pRetval; } bool SdrHdl::IsHdlHit(const Point& rPnt) const { // OVERLAYMANAGER basegfx::B2DPoint aPosition(rPnt.X(), rPnt.Y()); return maOverlayGroup.isHitLogic(aPosition); } PointerStyle SdrHdl::GetPointer() const { PointerStyle ePtr=PointerStyle::Move; const bool bSize=m_eKind>=SdrHdlKind::UpperLeft && m_eKind<=SdrHdlKind::LowerRight; const bool bRot=m_pHdlList!=nullptr && m_pHdlList->IsRotateShear(); const bool bDis=m_pHdlList!=nullptr && m_pHdlList->IsDistortShear(); if (bSize && m_pHdlList!=nullptr && (bRot || bDis)) { switch (m_eKind) { case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight: case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight: ePtr=bRot ? PointerStyle::Rotate : PointerStyle::RefHand; break; case SdrHdlKind::Left : case SdrHdlKind::Right: ePtr=PointerStyle::VShear; break; case SdrHdlKind::Upper: case SdrHdlKind::Lower: ePtr=PointerStyle::HShear; break; default: break; } } else { // When resizing rotated rectangles, rotate the mouse cursor slightly, too if (bSize && m_nRotationAngle!=0_deg100) { Degree100 nHdlAngle(0); switch (m_eKind) { case SdrHdlKind::LowerRight: nHdlAngle=31500_deg100; break; case SdrHdlKind::Lower: nHdlAngle=27000_deg100; break; case SdrHdlKind::LowerLeft: nHdlAngle=22500_deg100; break; case SdrHdlKind::Left : nHdlAngle=18000_deg100; break; case SdrHdlKind::UpperLeft: nHdlAngle=13500_deg100; break; case SdrHdlKind::Upper: nHdlAngle=9000_deg100; break; case SdrHdlKind::UpperRight: nHdlAngle=4500_deg100; break; case SdrHdlKind::Right: nHdlAngle=0_deg100; break; default: break; } // a little bit more (for rounding) nHdlAngle = NormAngle36000(nHdlAngle + m_nRotationAngle + 2249_deg100); nHdlAngle/=4500_deg100; switch (static_cast(nHdlAngle.get())) { case 0: ePtr=PointerStyle::ESize; break; case 1: ePtr=PointerStyle::NESize; break; case 2: ePtr=PointerStyle::NSize; break; case 3: ePtr=PointerStyle::NWSize; break; case 4: ePtr=PointerStyle::WSize; break; case 5: ePtr=PointerStyle::SWSize; break; case 6: ePtr=PointerStyle::SSize; break; case 7: ePtr=PointerStyle::SESize; break; } // switch } else { switch (m_eKind) { case SdrHdlKind::UpperLeft: ePtr=PointerStyle::NWSize; break; case SdrHdlKind::Upper: ePtr=PointerStyle::NSize; break; case SdrHdlKind::UpperRight: ePtr=PointerStyle::NESize; break; case SdrHdlKind::Left : ePtr=PointerStyle::WSize; break; case SdrHdlKind::Right: ePtr=PointerStyle::ESize; break; case SdrHdlKind::LowerLeft: ePtr=PointerStyle::SWSize; break; case SdrHdlKind::Lower: ePtr=PointerStyle::SSize; break; case SdrHdlKind::LowerRight: ePtr=PointerStyle::SESize; break; case SdrHdlKind::Poly : ePtr=PointerStyle::MovePoint; break; case SdrHdlKind::Circle : ePtr=PointerStyle::Hand; break; case SdrHdlKind::Ref1 : ePtr=PointerStyle::RefHand; break; case SdrHdlKind::Ref2 : ePtr=PointerStyle::RefHand; break; case SdrHdlKind::BezierWeight : ePtr=PointerStyle::MoveBezierWeight; break; case SdrHdlKind::Glue : ePtr=PointerStyle::MovePoint; break; case SdrHdlKind::CustomShape1 : ePtr=PointerStyle::RefHand; break; default: break; } } } return ePtr; } bool SdrHdl::IsFocusHdl() const { switch(m_eKind) { case SdrHdlKind::UpperLeft: case SdrHdlKind::Upper: case SdrHdlKind::UpperRight: case SdrHdlKind::Left: case SdrHdlKind::Right: case SdrHdlKind::LowerLeft: case SdrHdlKind::Lower: case SdrHdlKind::LowerRight: { // if it's an activated TextEdit, it's moved to extended points return !m_pHdlList || !m_pHdlList->IsMoveOutside(); } case SdrHdlKind::Move: // handle to move object case SdrHdlKind::Poly: // selected point of polygon or curve case SdrHdlKind::BezierWeight: // weight at a curve case SdrHdlKind::Circle: // angle of circle segments, corner radius of rectangles case SdrHdlKind::Ref1: // reference point 1, e. g. center of rotation case SdrHdlKind::Ref2: // reference point 2, e. g. endpoint of reflection axis case SdrHdlKind::Glue: // gluepoint // for SJ and the CustomShapeHandles: case SdrHdlKind::CustomShape1: case SdrHdlKind::User: { return true; } default: { return false; } } } void SdrHdl::onMouseEnter(const MouseEvent& /*rMEvt*/) { } void SdrHdl::onHelpRequest() { } void SdrHdl::onMouseLeave() { } BitmapEx SdrHdl::createGluePointBitmap() { return ImpGetBitmapEx(BitmapMarkerKind::Glue_Deselected, BitmapColorIndex::LightGreen); } void SdrHdl::insertNewlyCreatedOverlayObjectForSdrHdl( std::unique_ptr pOverlayObject, const sdr::contact::ObjectContact& rObjectContact, sdr::overlay::OverlayManager& rOverlayManager) { // check if we have an OverlayObject if(!pOverlayObject) { return; } // Add GridOffset for non-linear ViewToDevice transformation (calc) if(nullptr != GetObj() && rObjectContact.supportsGridOffsets()) { basegfx::B2DVector aOffset(0.0, 0.0); const sdr::contact::ViewObjectContact& rVOC(GetObj()->GetViewContact().GetViewObjectContact( const_cast(rObjectContact))); rObjectContact.calculateGridOffsetForViewObjectContact(aOffset, rVOC); if(!aOffset.equalZero()) { pOverlayObject->setOffset(aOffset); } } // add to OverlayManager rOverlayManager.add(*pOverlayObject); // add to local OverlayObjectList - ownership change (!) maOverlayGroup.append(std::move(pOverlayObject)); } SdrHdlColor::SdrHdlColor(const Point& rRef, Color aCol, const Size& rSize, bool bLum) : SdrHdl(rRef, SdrHdlKind::Color), m_aMarkerSize(rSize), m_bUseLuminance(bLum) { if(IsUseLuminance()) aCol = GetLuminance(aCol); // remember color m_aMarkerColor = aCol; } SdrHdlColor::~SdrHdlColor() { } void SdrHdlColor::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { BitmapEx aBmpCol(CreateColorDropper(m_aMarkerColor)); basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y()); std::unique_ptr pNewOverlayObject(new sdr::overlay::OverlayBitmapEx( aPosition, aBmpCol, static_cast(aBmpCol.GetSizePixel().Width() - 1) >> 1, static_cast(aBmpCol.GetSizePixel().Height() - 1) >> 1 )); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } BitmapEx SdrHdlColor::CreateColorDropper(Color aCol) { // get the Bitmap VclPtr pWrite(VclPtr::Create()); pWrite->SetOutputSizePixel(m_aMarkerSize); pWrite->SetBackground(aCol); pWrite->Erase(); // draw outer border sal_Int32 nWidth = m_aMarkerSize.Width(); sal_Int32 nHeight = m_aMarkerSize.Height(); pWrite->SetLineColor(COL_LIGHTGRAY); pWrite->DrawLine(Point(0, 0), Point(0, nHeight - 1)); pWrite->DrawLine(Point(1, 0), Point(nWidth - 1, 0)); pWrite->SetLineColor(COL_GRAY); pWrite->DrawLine(Point(1, nHeight - 1), Point(nWidth - 1, nHeight - 1)); pWrite->DrawLine(Point(nWidth - 1, 1), Point(nWidth - 1, nHeight - 2)); // draw lighter UpperLeft const Color aLightColor( static_cast(::std::min(static_cast(static_cast(aCol.GetRed()) + sal_Int16(0x0040)), sal_Int16(0x00ff))), static_cast(::std::min(static_cast(static_cast(aCol.GetGreen()) + sal_Int16(0x0040)), sal_Int16(0x00ff))), static_cast(::std::min(static_cast(static_cast(aCol.GetBlue()) + sal_Int16(0x0040)), sal_Int16(0x00ff)))); pWrite->SetLineColor(aLightColor); pWrite->DrawLine(Point(1, 1), Point(1, nHeight - 2)); pWrite->DrawLine(Point(2, 1), Point(nWidth - 2, 1)); // draw darker LowerRight const Color aDarkColor( static_cast(::std::max(static_cast(static_cast(aCol.GetRed()) - sal_Int16(0x0040)), sal_Int16(0x0000))), static_cast(::std::max(static_cast(static_cast(aCol.GetGreen()) - sal_Int16(0x0040)), sal_Int16(0x0000))), static_cast(::std::max(static_cast(static_cast(aCol.GetBlue()) - sal_Int16(0x0040)), sal_Int16(0x0000)))); pWrite->SetLineColor(aDarkColor); pWrite->DrawLine(Point(2, nHeight - 2), Point(nWidth - 2, nHeight - 2)); pWrite->DrawLine(Point(nWidth - 2, 2), Point(nWidth - 2, nHeight - 3)); return pWrite->GetBitmapEx(Point(0,0), m_aMarkerSize); } Color SdrHdlColor::GetLuminance(const Color& rCol) { sal_uInt8 aLum = rCol.GetLuminance(); Color aRetval(aLum, aLum, aLum); return aRetval; } void SdrHdlColor::SetColor(Color aNew, bool bCallLink) { if(IsUseLuminance()) aNew = GetLuminance(aNew); if(m_aMarkerColor != aNew) { // remember new color m_aMarkerColor = aNew; // create new display Touch(); // tell about change if(bCallLink) m_aColorChangeHdl.Call(this); } } void SdrHdlColor::SetSize(const Size& rNew) { if(rNew != m_aMarkerSize) { // remember new size m_aMarkerSize = rNew; // create new display Touch(); } } SdrHdlGradient::SdrHdlGradient(const Point& rRef1, const Point& rRef2, bool bGrad) : SdrHdl(rRef1, bGrad ? SdrHdlKind::Gradient : SdrHdlKind::Transparence) , m_pColHdl1(nullptr) , m_pColHdl2(nullptr) , m_a2ndPos(rRef2) , m_bGradient(bGrad) , m_bMoveSingleHandle(false) , m_bMoveFirstHandle(false) { } SdrHdlGradient::~SdrHdlGradient() { } void SdrHdlGradient::Set2ndPos(const Point& rPnt) { if(m_a2ndPos != rPnt) { // remember new position m_a2ndPos = rPnt; // create new display Touch(); } } void SdrHdlGradient::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { // striped line in between basegfx::B2DVector aVec(m_a2ndPos.X() - m_aPos.X(), m_a2ndPos.Y() - m_aPos.Y()); double fVecLen = aVec.getLength(); double fLongPercentArrow = (1.0 - 0.05) * fVecLen; double fHalfArrowWidth = (0.05 * 0.5) * fVecLen; aVec.normalize(); basegfx::B2DVector aPerpend(-aVec.getY(), aVec.getX()); sal_Int32 nMidX = static_cast(m_aPos.X() + aVec.getX() * fLongPercentArrow); sal_Int32 nMidY = static_cast(m_aPos.Y() + aVec.getY() * fLongPercentArrow); Point aMidPoint(nMidX, nMidY); basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y()); basegfx::B2DPoint aMidPos(aMidPoint.X(), aMidPoint.Y()); std::unique_ptr pNewOverlayObject(new sdr::overlay::OverlayLineStriped( aPosition, aMidPos )); pNewOverlayObject->setBaseColor(IsGradient() ? COL_BLACK : COL_BLUE); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); // arrowhead Point aLeft(aMidPoint.X() + static_cast(aPerpend.getX() * fHalfArrowWidth), aMidPoint.Y() + static_cast(aPerpend.getY() * fHalfArrowWidth)); Point aRight(aMidPoint.X() - static_cast(aPerpend.getX() * fHalfArrowWidth), aMidPoint.Y() - static_cast(aPerpend.getY() * fHalfArrowWidth)); basegfx::B2DPoint aPositionLeft(aLeft.X(), aLeft.Y()); basegfx::B2DPoint aPositionRight(aRight.X(), aRight.Y()); basegfx::B2DPoint aPosition2(m_a2ndPos.X(), m_a2ndPos.Y()); pNewOverlayObject.reset(new sdr::overlay::OverlayTriangle( aPositionLeft, aPosition2, aPositionRight, IsGradient() ? COL_BLACK : COL_BLUE )); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } IMPL_LINK_NOARG(SdrHdlGradient, ColorChangeHdl, SdrHdlColor*, void) { if(GetObj()) FromIAOToItem(GetObj(), true, true); } void SdrHdlGradient::FromIAOToItem(SdrObject* _pObj, bool bSetItemOnObject, bool bUndo) { // from IAO positions and colors to gradient const SfxItemSet& rSet = _pObj->GetMergedItemSet(); GradTransGradient aOldGradTransGradient; GradTransGradient aGradTransGradient; GradTransVector aGradTransVector; aGradTransVector.maPositionA = basegfx::B2DPoint(GetPos().X(), GetPos().Y()); aGradTransVector.maPositionB = basegfx::B2DPoint(Get2ndPos().X(), Get2ndPos().Y()); if(m_pColHdl1) aGradTransVector.aCol1 = m_pColHdl1->GetColor(); if(m_pColHdl2) aGradTransVector.aCol2 = m_pColHdl2->GetColor(); if(IsGradient()) aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue(); else aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue(); // transform vector data to gradient GradTransformer::VecToGrad(aGradTransVector, aGradTransGradient, aOldGradTransGradient, _pObj, m_bMoveSingleHandle, m_bMoveFirstHandle); if(bSetItemOnObject) { SdrModel& rModel(_pObj->getSdrModelFromSdrObject()); SfxItemSet aNewSet(rModel.GetItemPool()); const OUString aString; if(IsGradient()) { XFillGradientItem aNewGradItem(aString, aGradTransGradient.aGradient); aNewSet.Put(aNewGradItem); } else { XFillFloatTransparenceItem aNewTransItem(aString, aGradTransGradient.aGradient); aNewSet.Put(aNewTransItem); } if(bUndo && rModel.IsUndoEnabled()) { rModel.BegUndo(SvxResId(IsGradient() ? SIP_XA_FILLGRADIENT : SIP_XA_FILLTRANSPARENCE)); rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(*_pObj)); rModel.EndUndo(); } m_pObj->SetMergedItemSetAndBroadcast(aNewSet); } // back transformation, set values on pIAOHandle GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, _pObj); SetPos({ basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()) }); Set2ndPos({ basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()) }); if(m_pColHdl1) { m_pColHdl1->SetPos({ basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()) }); m_pColHdl1->SetColor(aGradTransVector.aCol1); } if(m_pColHdl2) { m_pColHdl2->SetPos({ basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()) }); m_pColHdl2->SetColor(aGradTransVector.aCol2); } } SdrHdlLine::~SdrHdlLine() {} void SdrHdlLine::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!(pView && !pView->areMarkHandlesHidden() && m_pHdl1 && m_pHdl2)) return; SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { basegfx::B2DPoint aPosition1(m_pHdl1->GetPos().X(), m_pHdl1->GetPos().Y()); basegfx::B2DPoint aPosition2(m_pHdl2->GetPos().X(), m_pHdl2->GetPos().Y()); std::unique_ptr pNewOverlayObject(new sdr::overlay::OverlayLineStriped( aPosition1, aPosition2 )); // color(?) pNewOverlayObject->setBaseColor(COL_LIGHTRED); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } PointerStyle SdrHdlLine::GetPointer() const { return PointerStyle::RefHand; } SdrHdlBezWgt::~SdrHdlBezWgt() {} void SdrHdlBezWgt::CreateB2dIAObject() { // call parent SdrHdl::CreateB2dIAObject(); // create lines if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { basegfx::B2DPoint aPosition1(m_pHdl1->GetPos().X(), m_pHdl1->GetPos().Y()); basegfx::B2DPoint aPosition2(m_aPos.X(), m_aPos.Y()); if(!aPosition1.equal(aPosition2)) { std::unique_ptr pNewOverlayObject(new sdr::overlay::OverlayLineStriped( aPosition1, aPosition2 )); // line part is not hittable pNewOverlayObject->setHittable(false); // color(?) pNewOverlayObject->setBaseColor(COL_LIGHTBLUE); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } } E3dVolumeMarker::E3dVolumeMarker(const basegfx::B2DPolyPolygon& rWireframePoly) { m_aWireframePoly = rWireframePoly; } void E3dVolumeMarker::CreateB2dIAObject() { // create lines if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is() && m_aWireframePoly.count()) { std::unique_ptr pNewOverlayObject(new sdr::overlay::OverlayPolyPolygonStripedAndFilled( m_aWireframePoly)); pNewOverlayObject->setBaseColor(COL_BLACK); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } ImpEdgeHdl::~ImpEdgeHdl() { } void ImpEdgeHdl::CreateB2dIAObject() { if(m_nObjHdlNum > 1 || !m_pObj) { // call parent SdrHdl::CreateB2dIAObject(); return; } // first throw away old one GetRidOfIAObject(); BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan; BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7; if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; // tdf#159666 Crash when table and line object are selected at the same time auto pEdge = dynamic_cast(m_pObj); if (!pEdge) return; if(pEdge->GetConnectedNode(m_nObjHdlNum == 0) != nullptr) eColIndex = BitmapColorIndex::LightRed; if(m_nPPntNum < 2) { // Handle with plus sign inside eKindOfMarker = BitmapMarkerKind::Circ_7x7; } SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y()); std::unique_ptr pNewOverlayObject(CreateOverlayObject( aPosition, eColIndex, eKindOfMarker)); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } void ImpEdgeHdl::SetLineCode(SdrEdgeLineCode eCode) { if(m_eLineCode != eCode) { // remember new value m_eLineCode = eCode; // create new display Touch(); } } PointerStyle ImpEdgeHdl::GetPointer() const { SdrEdgeObj* pEdge=dynamic_cast( m_pObj ); if (pEdge==nullptr) return SdrHdl::GetPointer(); if (m_nObjHdlNum<=1) return PointerStyle::MovePoint; if (IsHorzDrag()) return PointerStyle::ESize; else return PointerStyle::SSize; } bool ImpEdgeHdl::IsHorzDrag() const { SdrEdgeObj* pEdge=dynamic_cast( m_pObj ); if (pEdge==nullptr) return false; if (m_nObjHdlNum<=1) return false; SdrEdgeKind eEdgeKind = pEdge->GetObjectItem(SDRATTR_EDGEKIND).GetValue(); const SdrEdgeInfoRec& rInfo=pEdge->m_aEdgeInfo; if (eEdgeKind==SdrEdgeKind::OrthoLines || eEdgeKind==SdrEdgeKind::Bezier) { return !rInfo.ImpIsHorzLine(m_eLineCode,*pEdge->m_pEdgeTrack); } else if (eEdgeKind==SdrEdgeKind::ThreeLines) { tools::Long nAngle=m_nObjHdlNum==2 ? rInfo.m_nAngle1 : rInfo.m_nAngle2; return nAngle==0 || nAngle==18000; } return false; } ImpMeasureHdl::~ImpMeasureHdl() { } void ImpMeasureHdl::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan; BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_9x9; if(m_nObjHdlNum > 1) { eKindOfMarker = BitmapMarkerKind::Rect_7x7; } if(m_bSelect) { eColIndex = BitmapColorIndex::Cyan; } SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y()); std::unique_ptr pNewOverlayObject(CreateOverlayObject( aPosition, eColIndex, eKindOfMarker)); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } PointerStyle ImpMeasureHdl::GetPointer() const { switch (m_nObjHdlNum) { case 0: case 1: return PointerStyle::Hand; case 2: case 3: return PointerStyle::MovePoint; case 4: case 5: return SdrHdl::GetPointer(); // will then be rotated appropriately } // switch return PointerStyle::NotAllowed; } ImpTextframeHdl::ImpTextframeHdl(const tools::Rectangle& rRect) : SdrHdl(rRect.TopLeft(),SdrHdlKind::Move), maRect(rRect) { } void ImpTextframeHdl::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if(!m_pHdlList) return; SdrMarkView* pView = m_pHdlList->GetView(); if(!pView || pView->areMarkHandlesHidden()) return; SdrPageView* pPageView = pView->GetSdrPageView(); if(!pPageView) return; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { const basegfx::B2DPoint aTopLeft(maRect.Left(), maRect.Top()); const basegfx::B2DPoint aBottomRight(maRect.Right(), maRect.Bottom()); const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor()); const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); std::unique_ptr pNewOverlayObject(new sdr::overlay::OverlayRectangle( aTopLeft, aBottomRight, aHilightColor, fTransparence, 3.0, 3.0, -toRadians(m_nRotationAngle), true)); // allow animation; the Handle is not shown at text edit time pNewOverlayObject->setHittable(false); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNewOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } static bool ImpSdrHdlListSorter(std::unique_ptr const& lhs, std::unique_ptr const& rhs) { SdrHdlKind eKind1=lhs->GetKind(); SdrHdlKind eKind2=rhs->GetKind(); // Level 1: first normal handles, then Glue, then User, then Plus handles, then reference point handles unsigned n1=1; unsigned n2=1; if (eKind1!=eKind2) { if (eKind1==SdrHdlKind::Ref1 || eKind1==SdrHdlKind::Ref2 || eKind1==SdrHdlKind::MirrorAxis) n1=5; else if (eKind1==SdrHdlKind::Glue) n1=2; else if (eKind1==SdrHdlKind::User) n1=3; else if (eKind1==SdrHdlKind::SmartTag) n1=0; if (eKind2==SdrHdlKind::Ref1 || eKind2==SdrHdlKind::Ref2 || eKind2==SdrHdlKind::MirrorAxis) n2=5; else if (eKind2==SdrHdlKind::Glue) n2=2; else if (eKind2==SdrHdlKind::User) n2=3; else if (eKind2==SdrHdlKind::SmartTag) n2=0; } if (lhs->IsPlusHdl()) n1=4; if (rhs->IsPlusHdl()) n2=4; if (n1==n2) { // Level 2: PageView (Pointer) SdrPageView* pPV1=lhs->GetPageView(); SdrPageView* pPV2=rhs->GetPageView(); if (pPV1==pPV2) { // Level 3: Position (x+y) SdrObject* pObj1=lhs->GetObj(); SdrObject* pObj2=rhs->GetObj(); if (pObj1==pObj2) { sal_uInt32 nNum1=lhs->GetObjHdlNum(); sal_uInt32 nNum2=rhs->GetObjHdlNum(); if (nNum1==nNum2) { if (eKind1==eKind2) return lhs(eKind1)(eKind2); } else return nNum1(pVoid1); const ImplHdlAndIndex* p2 = static_cast(pVoid2); if(p1->mpHdl->GetObj() == p2->mpHdl->GetObj()) { if(p1->mpHdl->GetObj() && dynamic_cast(p1->mpHdl->GetObj()) != nullptr) { // same object and a path object if((p1->mpHdl->GetKind() == SdrHdlKind::Poly || p1->mpHdl->GetKind() == SdrHdlKind::BezierWeight) && (p2->mpHdl->GetKind() == SdrHdlKind::Poly || p2->mpHdl->GetKind() == SdrHdlKind::BezierWeight)) { // both handles are point or control handles if(p1->mpHdl->GetPolyNum() == p2->mpHdl->GetPolyNum()) { if(p1->mpHdl->GetPointNum() < p2->mpHdl->GetPointNum()) { return -1; } else { return 1; } } else if(p1->mpHdl->GetPolyNum() < p2->mpHdl->GetPolyNum()) { return -1; } else { return 1; } } } } else { if(!p1->mpHdl->GetObj()) { return -1; } else if(!p2->mpHdl->GetObj()) { return 1; } else { // different objects, use OrdNum for sort const sal_uInt32 nOrdNum1 = p1->mpHdl->GetObj()->GetOrdNum(); const sal_uInt32 nOrdNum2 = p2->mpHdl->GetObj()->GetOrdNum(); if(nOrdNum1 < nOrdNum2) { return -1; } else { return 1; } } } // fallback to indices if(p1->mnIndex < p2->mnIndex) { return -1; } else { return 1; } } } void SdrHdlList::TravelFocusHdl(bool bForward) { // security correction if (mnFocusIndex >= GetHdlCount()) mnFocusIndex = SAL_MAX_SIZE; if(maList.empty()) return; // take care of old handle const size_t nOldHdlNum(mnFocusIndex); SdrHdl* pOld = nullptr; if (nOldHdlNum < GetHdlCount()) pOld = GetHdl(nOldHdlNum); if(pOld) { // switch off old handle mnFocusIndex = SAL_MAX_SIZE; pOld->Touch(); } // allocate pointer array for sorted handle list std::unique_ptr pHdlAndIndex(new ImplHdlAndIndex[maList.size()]); // build sorted handle list for( size_t a = 0; a < maList.size(); ++a) { pHdlAndIndex[a].mpHdl = maList[a].get(); pHdlAndIndex[a].mnIndex = a; } qsort(pHdlAndIndex.get(), maList.size(), sizeof(ImplHdlAndIndex), ImplSortHdlFunc); // look for old num in sorted array size_t nOldHdl(nOldHdlNum); if(nOldHdlNum != SAL_MAX_SIZE) { for(size_t a = 0; a < maList.size(); ++a) { if(pHdlAndIndex[a].mpHdl == pOld) { nOldHdl = a; break; } } } // build new HdlNum size_t nNewHdl(nOldHdl); // do the focus travel if(bForward) { if(nOldHdl != SAL_MAX_SIZE) { if(nOldHdl == maList.size() - 1) { // end forward run nNewHdl = SAL_MAX_SIZE; } else { // simply the next handle nNewHdl++; } } else { // start forward run at first entry nNewHdl = 0; } } else { if(nOldHdl == SAL_MAX_SIZE) { // start backward run at last entry nNewHdl = maList.size() - 1; } else { if(nOldHdl == 0) { // end backward run nNewHdl = SAL_MAX_SIZE; } else { // simply the previous handle nNewHdl--; } } } // build new HdlNum sal_uIntPtr nNewHdlNum(nNewHdl); // look for old num in sorted array if(nNewHdl != SAL_MAX_SIZE) { SdrHdl* pNew = pHdlAndIndex[nNewHdl].mpHdl; for(size_t a = 0; a < maList.size(); ++a) { if(maList[a].get() == pNew) { nNewHdlNum = a; break; } } } // take care of next handle if(nOldHdlNum != nNewHdlNum) { mnFocusIndex = nNewHdlNum; if (mnFocusIndex < GetHdlCount()) { SdrHdl* pNew = GetHdl(mnFocusIndex); pNew->Touch(); } } } SdrHdl* SdrHdlList::GetFocusHdl() const { if(mnFocusIndex < GetHdlCount()) return GetHdl(mnFocusIndex); else return nullptr; } void SdrHdlList::SetFocusHdl(SdrHdl* pNew) { if(!pNew) return; SdrHdl* pActual = GetFocusHdl(); if(pActual && pActual == pNew) return; const size_t nNewHdlNum = GetHdlNum(pNew); if(nNewHdlNum != SAL_MAX_SIZE) { mnFocusIndex = nNewHdlNum; if(pActual) { pActual->Touch(); } pNew->Touch(); } } void SdrHdlList::ResetFocusHdl() { SdrHdl* pHdl = GetFocusHdl(); mnFocusIndex = SAL_MAX_SIZE; if(pHdl) { pHdl->Touch(); } } SdrHdlList::SdrHdlList(SdrMarkView* pV) : mnFocusIndex(SAL_MAX_SIZE), m_pView(pV) { m_nHdlSize = 3; m_bRotateShear = false; m_bMoveOutside = false; m_bDistortShear = false; } SdrHdlList::~SdrHdlList() { Clear(); } void SdrHdlList::SetHdlSize(sal_uInt16 nSiz) { if(m_nHdlSize != nSiz) { // remember new value m_nHdlSize = nSiz; // propagate change to IAOs for(size_t i=0; iTouch(); } } } void SdrHdlList::SetMoveOutside(bool bOn) { if(m_bMoveOutside != bOn) { // remember new value m_bMoveOutside = bOn; // propagate change to IAOs for(size_t i=0; iTouch(); } } } void SdrHdlList::SetRotateShear(bool bOn) { m_bRotateShear = bOn; } void SdrHdlList::SetDistortShear(bool bOn) { m_bDistortShear = bOn; } std::unique_ptr SdrHdlList::RemoveHdl(size_t nNum) { std::unique_ptr pRetval = std::move(maList[nNum]); maList.erase(maList.begin() + nNum); return pRetval; } void SdrHdlList::RemoveAllByKind(SdrHdlKind eKind) { std::erase_if(maList, [&eKind](std::unique_ptr& rItem) { return rItem->GetKind() == eKind; }); } void SdrHdlList::Clear() { maList.clear(); m_bRotateShear=false; m_bDistortShear=false; } void SdrHdlList::Sort() { // remember currently focused handle SdrHdl* pPrev = GetFocusHdl(); std::sort( maList.begin(), maList.end(), ImpSdrHdlListSorter ); // get now and compare SdrHdl* pNow = GetFocusHdl(); if(pPrev == pNow) return; if(pPrev) { pPrev->Touch(); } if(pNow) { pNow->Touch(); } } size_t SdrHdlList::GetHdlNum(const SdrHdl* pHdl) const { if (pHdl==nullptr) return SAL_MAX_SIZE; auto it = std::find_if( maList.begin(), maList.end(), [&](const std::unique_ptr & p) { return p.get() == pHdl; }); assert(it != maList.end()); if( it == maList.end() ) return SAL_MAX_SIZE; return it - maList.begin(); } void SdrHdlList::AddHdl(std::unique_ptr pHdl) { assert(pHdl); pHdl->SetHdlList(this); maList.push_back(std::move(pHdl)); } SdrHdl* SdrHdlList::IsHdlListHit(const Point& rPnt) const { SdrHdl* pRet=nullptr; const size_t nCount=GetHdlCount(); size_t nNum=nCount; while (nNum>0 && pRet==nullptr) { nNum--; SdrHdl* pHdl=GetHdl(nNum); if (pHdl->IsHdlHit(rPnt)) pRet=pHdl; } return pRet; } SdrHdl* SdrHdlList::GetHdl(SdrHdlKind eKind1) const { SdrHdl* pRet=nullptr; for (size_t i=0; iGetKind()==eKind1) pRet=pHdl; } return pRet; } void SdrHdlList::MoveTo(SdrHdlList& rOther) { for (auto & pHdl : maList) pHdl->SetHdlList(&rOther); rOther.maList.insert(rOther.maList.end(), std::make_move_iterator(maList.begin()), std::make_move_iterator(maList.end())); maList.clear(); } SdrCropHdl::SdrCropHdl( const Point& rPnt, SdrHdlKind eNewKind, double fShearX, double fRotation) : SdrHdl(rPnt, eNewKind), mfShearX(fShearX), mfRotation(fRotation) { } BitmapEx SdrCropHdl::GetBitmapForHandle( const BitmapEx& rBitmap, int nSize ) { int nPixelSize = 0, nX = 0, nY = 0, nOffset = 0; if( nSize <= 3 ) { nPixelSize = 13; nOffset = 0; } else if( nSize <=4 ) { nPixelSize = 17; nOffset = 39; } else { nPixelSize = 21; nOffset = 90; } switch( m_eKind ) { case SdrHdlKind::UpperLeft: nX = 0; nY = 0; break; case SdrHdlKind::Upper: nX = 1; nY = 0; break; case SdrHdlKind::UpperRight: nX = 2; nY = 0; break; case SdrHdlKind::Left: nX = 0; nY = 1; break; case SdrHdlKind::Right: nX = 2; nY = 1; break; case SdrHdlKind::LowerLeft: nX = 0; nY = 2; break; case SdrHdlKind::Lower: nX = 1; nY = 2; break; case SdrHdlKind::LowerRight: nX = 2; nY = 2; break; default: break; } tools::Rectangle aSourceRect( Point( nX * nPixelSize + nOffset, nY * nPixelSize), Size(nPixelSize, nPixelSize) ); BitmapEx aRetval(rBitmap); aRetval.Crop(aSourceRect); return aRetval; } void SdrCropHdl::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); SdrMarkView* pView = m_pHdlList ? m_pHdlList->GetView() : nullptr; SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr; if( !pPageView || pView->areMarkHandlesHidden() ) return; const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); int nHdlSize = m_pHdlList->GetHdlSize(); const BitmapEx aHandlesBitmap(SIP_SA_CROP_MARKERS); BitmapEx aBmpEx1( GetBitmapForHandle( aHandlesBitmap, nHdlSize ) ); for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if (xManager.is()) { basegfx::B2DPoint aPosition(m_aPos.X(), m_aPos.Y()); std::unique_ptr pOverlayObject; // animate focused handles if(IsFocusHdl() && (m_pHdlList->GetFocusHdl() == this)) { if( nHdlSize >= 2 ) nHdlSize = 1; BitmapEx aBmpEx2( GetBitmapForHandle( aHandlesBitmap, nHdlSize + 1 ) ); const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime(); pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx( aPosition, aBmpEx1, aBmpEx2, nBlinkTime, static_cast(aBmpEx1.GetSizePixel().Width() - 1) >> 1, static_cast(aBmpEx1.GetSizePixel().Height() - 1) >> 1, static_cast(aBmpEx2.GetSizePixel().Width() - 1) >> 1, static_cast(aBmpEx2.GetSizePixel().Height() - 1) >> 1, mfShearX, mfRotation)); } else { // create centered handle as default pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx( aPosition, aBmpEx1, static_cast(aBmpEx1.GetSizePixel().Width() - 1) >> 1, static_cast(aBmpEx1.GetSizePixel().Height() - 1) >> 1, 0.0, mfShearX, mfRotation)); } // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pOverlayObject), rPageWindow.GetObjectContact(), *xManager); } } } } // with the correction of crop handling I could get rid of the extra mirroring flag, adapted stuff // accordingly SdrCropViewHdl::SdrCropViewHdl( basegfx::B2DHomMatrix aObjectTransform, Graphic aGraphic, double fCropLeft, double fCropTop, double fCropRight, double fCropBottom) : SdrHdl(Point(), SdrHdlKind::User), maObjectTransform(std::move(aObjectTransform)), maGraphic(std::move(aGraphic)), mfCropLeft(fCropLeft), mfCropTop(fCropTop), mfCropRight(fCropRight), mfCropBottom(fCropBottom) { } namespace { void translateRotationToMirroring(basegfx::B2DVector & scale, double * rotate) { assert(rotate != nullptr); // detect 180 degree rotation, this is the same as mirrored in X and Y, // thus change to mirroring. Prefer mirroring here. Use the equal call // with getSmallValue here, the original which uses rtl::math::approxEqual // is too correct here. Maybe this changes with enhanced precision in aw080 // to the better so that this can be reduced to the more precise call again if(basegfx::fTools::equal(fabs(*rotate), M_PI, 0.000000001)) { scale.setX(scale.getX() * -1.0); scale.setY(scale.getY() * -1.0); *rotate = 0.0; } } } void SdrCropViewHdl::CreateB2dIAObject() { GetRidOfIAObject(); SdrMarkView* pView = m_pHdlList ? m_pHdlList->GetView() : nullptr; SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr; if(!pPageView || pView->areMarkHandlesHidden()) { return; } // decompose to have current translate and scale basegfx::B2DVector aScale, aTranslate; double fRotate, fShearX; maObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); if(aScale.equalZero()) { return; } translateRotationToMirroring(aScale, &fRotate); // remember mirroring, reset at Scale and adapt crop values for usage; // mirroring can stay in the object transformation, so do not have to // cope with it here (except later for the CroppedImage transformation, // see below) const bool bMirroredX(aScale.getX() < 0.0); const bool bMirroredY(aScale.getY() < 0.0); double fCropLeft(mfCropLeft); double fCropTop(mfCropTop); double fCropRight(mfCropRight); double fCropBottom(mfCropBottom); if(bMirroredX) { aScale.setX(-aScale.getX()); } if(bMirroredY) { aScale.setY(-aScale.getY()); } // create target translate and scale const basegfx::B2DVector aTargetScale( aScale.getX() + fCropRight + fCropLeft, aScale.getY() + fCropBottom + fCropTop); const basegfx::B2DVector aTargetTranslate( aTranslate.getX() - fCropLeft, aTranslate.getY() - fCropTop); // create ranges to make comparisons const basegfx::B2DRange aCurrentForCompare( aTranslate.getX(), aTranslate.getY(), aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY()); basegfx::B2DRange aCropped( aTargetTranslate.getX(), aTargetTranslate.getY(), aTargetTranslate.getX() + aTargetScale.getX(), aTargetTranslate.getY() + aTargetScale.getY()); if(aCropped.isEmpty()) { // nothing to return since cropped content is completely empty return; } if(aCurrentForCompare.equal(aCropped)) { // no crop at all return; } // back-transform to have values in unit coordinates basegfx::B2DHomMatrix aBackToUnit; aBackToUnit.translate(-aTranslate.getX(), -aTranslate.getY()); aBackToUnit.scale( basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : 1.0 / aScale.getX(), basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : 1.0 / aScale.getY()); // transform cropped back to unit coordinates aCropped.transform(aBackToUnit); // prepare crop PolyPolygon basegfx::B2DPolygon aGraphicOutlinePolygon( basegfx::utils::createPolygonFromRect( aCropped)); basegfx::B2DPolyPolygon aCropPolyPolygon(aGraphicOutlinePolygon); // current range is unit range basegfx::B2DRange aOverlap(0.0, 0.0, 1.0, 1.0); aOverlap.intersect(aCropped); if(!aOverlap.isEmpty()) { aCropPolyPolygon.append( basegfx::utils::createPolygonFromRect( aOverlap)); } // transform to object coordinates to prepare for clip aCropPolyPolygon.transform(maObjectTransform); aGraphicOutlinePolygon.transform(maObjectTransform); // create cropped transformation basegfx::B2DHomMatrix aCroppedTransform; aCroppedTransform.scale( aCropped.getWidth(), aCropped.getHeight()); aCroppedTransform.translate( aCropped.getMinX(), aCropped.getMinY()); aCroppedTransform = maObjectTransform * aCroppedTransform; // prepare graphic primitive (transformed) const drawinglayer::primitive2d::Primitive2DReference aGraphic( new drawinglayer::primitive2d::GraphicPrimitive2D( aCroppedTransform, maGraphic)); // prepare outline polygon for whole graphic const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor()); const drawinglayer::primitive2d::Primitive2DReference aGraphicOutline( new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( std::move(aGraphicOutlinePolygon), aHilightColor)); // combine these drawinglayer::primitive2d::Primitive2DContainer aCombination { aGraphic, aGraphicOutline }; // embed to MaskPrimitive2D const drawinglayer::primitive2d::Primitive2DReference aMaskedGraphic( new drawinglayer::primitive2d::MaskPrimitive2D( std::move(aCropPolyPolygon), std::move(aCombination))); // embed to UnifiedTransparencePrimitive2D const drawinglayer::primitive2d::Primitive2DReference aTransparenceMaskedGraphic( new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( drawinglayer::primitive2d::Primitive2DContainer { aMaskedGraphic }, 0.8)); const drawinglayer::primitive2d::Primitive2DContainer aSequence { aTransparenceMaskedGraphic }; for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) { // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b]; const SdrPageWindow& rPageWindow = *(pPageView->GetPageWindow(b)); if(rPageWindow.GetPaintWindow().OutputToWindow()) { const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); if(xManager.is()) { std::unique_ptr pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(drawinglayer::primitive2d::Primitive2DContainer(aSequence))); // only informative object, no hit pNew->setHittable(false); // OVERLAYMANAGER insertNewlyCreatedOverlayObjectForSdrHdl( std::move(pNew), rPageWindow.GetObjectContact(), *xManager); } } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */