/* -*- 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 <algorithm>
#include <cassert>
#include <svx/svdhdl.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdmrkv.hxx>
#include <utility>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <vcl/ptrstyle.hxx>
#include <svx/sxekitm.hxx>
#include <svx/strings.hrc>
#include <svx/svdmodel.hxx>
#include "gradtrns.hxx"
#include <svx/xflgrit.hxx>
#include <svx/svdundo.hxx>
#include <svx/dialmgr.hxx>
#include <svx/xflftrit.hxx>
#include <svx/svdopath.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
#include <svx/sdr/overlay/overlaybitmapex.hxx>
#include <sdr/overlay/overlayline.hxx>
#include <sdr/overlay/overlaytriangle.hxx>
#include <sdr/overlay/overlayhandle.hxx>
#include <sdr/overlay/overlayrectangle.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <vcl/svapp.hxx>
#include <svx/sdr/overlay/overlaypolypolygon.hxx>
#include <tools/lazydelete.hxx>
#include <vcl/BitmapTools.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <osl/diagnose.h>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <memory>
#include <bitmaps.hlst>
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<sdr::overlay::OverlayObject> 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<sdr::overlay::OverlayObject> SdrHdl::CreateOverlayObject(
const basegfx::B2DPoint& rPos,
BitmapColorIndex eColIndex, BitmapMarkerKind eKindOfMarker, Point aMoveOutsideOffset)
{
std::unique_ptr<sdr::overlay::OverlayObject> 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<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1), 0,
static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1), 0));
}
else
{
// create centered handle as default
pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime,
static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
static_cast<sal_uInt16>(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<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1), 0));
}
else
{
sal_uInt16 nCenX(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1) >> 1);
sal_uInt16 nCenY(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1) >> 1);
if(aMoveOutsideOffset.X() > 0)
{
nCenX = 0;
}
else if(aMoveOutsideOffset.X() < 0)
{
nCenX = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1);
}
if(aMoveOutsideOffset.Y() > 0)
{
nCenY = 0;
}
else if(aMoveOutsideOffset.Y() < 0)
{
nCenY = static_cast<sal_uInt16>(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<sal_uInt8>(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<sdr::overlay::OverlayObject> 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<sdr::contact::ObjectContact&>(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<sdr::overlay::OverlayObject> pNewOverlayObject(new
sdr::overlay::OverlayBitmapEx(
aPosition,
aBmpCol,
static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Width() - 1) >> 1,
static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Height() - 1) >> 1
));
// OVERLAYMANAGER
insertNewlyCreatedOverlayObjectForSdrHdl(
std::move(pNewOverlayObject),
rPageWindow.GetObjectContact(),
*xManager);
}
}
}
}
BitmapEx SdrHdlColor::CreateColorDropper(Color aCol)
{
// get the Bitmap
VclPtr<VirtualDevice> pWrite(VclPtr<VirtualDevice>::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<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(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<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(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<sal_Int32>(m_aPos.X() + aVec.getX() * fLongPercentArrow);
sal_Int32 nMidY = static_cast<sal_Int32>(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<sdr::overlay::OverlayObject> 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<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
aMidPoint.Y() + static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));
Point aRight(aMidPoint.X() - static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
aMidPoint.Y() - static_cast<sal_Int32>(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<tools::Long>(aGradTransVector.maPositionA.getX()),
basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getY()) });
Set2ndPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getX()),
basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getY()) });
if(m_pColHdl1)
{
m_pColHdl1->SetPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getX()),
basegfx::fround<tools::Long>(aGradTransVector.maPositionA.getY()) });
m_pColHdl1->SetColor(aGradTransVector.aCol1);
}
if(m_pColHdl2)
{
m_pColHdl2->SetPos({ basegfx::fround<tools::Long>(aGradTransVector.maPositionB.getX()),
basegfx::fround<tools::Long>(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<sdr::overlay::OverlayObject> 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<sdr::overlay::OverlayObject> 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<sdr::overlay::OverlayObject> 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<SdrEdgeObj*>(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<sdr::overlay::OverlayObject> 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<SdrEdgeObj*>( 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<SdrEdgeObj*>( 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<sdr::overlay::OverlayObject> 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<sdr::overlay::OverlayRectangle> 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<SdrHdl> const& lhs, std::unique_ptr<SdrHdl> 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<rhs; // Hack, to always get to the same sorting
return static_cast<sal_uInt16>(eKind1)<static_cast<sal_uInt16>(eKind2);
}
else
return nNum1<nNum2;
}
else
{
return pObj1<pObj2;
}
}
else
{
return pPV1<pPV2;
}
}
else
{
return n1<n2;
}
}
namespace {
// Helper struct for re-sorting handles
struct ImplHdlAndIndex
{
SdrHdl* mpHdl;
sal_uInt32 mnIndex;
};
}
extern "C" {
// Helper method for sorting handles taking care of OrdNums, keeping order in
// single objects and re-sorting polygon handles intuitively
static int ImplSortHdlFunc( const void* pVoid1, const void* pVoid2 )
{
const ImplHdlAndIndex* p1 = static_cast<ImplHdlAndIndex const *>(pVoid1);
const ImplHdlAndIndex* p2 = static_cast<ImplHdlAndIndex const *>(pVoid2);
if(p1->mpHdl->GetObj() == p2->mpHdl->GetObj())
{
if(p1->mpHdl->GetObj() && dynamic_cast<const SdrPathObj*>(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<ImplHdlAndIndex[]> 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; i<GetHdlCount(); ++i)
{
SdrHdl* pHdl = GetHdl(i);
pHdl->Touch();
}
}
}
void SdrHdlList::SetMoveOutside(bool bOn)
{
if(m_bMoveOutside != bOn)
{
// remember new value
m_bMoveOutside = bOn;
// propagate change to IAOs
for(size_t i=0; i<GetHdlCount(); ++i)
{
SdrHdl* pHdl = GetHdl(i);
pHdl->Touch();
}
}
}
void SdrHdlList::SetRotateShear(bool bOn)
{
m_bRotateShear = bOn;
}
void SdrHdlList::SetDistortShear(bool bOn)
{
m_bDistortShear = bOn;
}
std::unique_ptr<SdrHdl> SdrHdlList::RemoveHdl(size_t nNum)
{
std::unique_ptr<SdrHdl> 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<SdrHdl>& 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<SdrHdl> & 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<SdrHdl> 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; i<GetHdlCount() && pRet==nullptr; ++i)
{
SdrHdl* pHdl=GetHdl(i);
if (pHdl->GetKind()==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<sdr::overlay::OverlayObject> 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<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1,
mfShearX,
mfRotation));
}
else
{
// create centered handle as default
pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx(
aPosition,
aBmpEx1,
static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
static_cast<sal_uInt16>(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<sdr::overlay::OverlayObject> 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: */
↑ V530 The return value of function 'normalize' is required to be utilized.
↑ V1004 The 'pView' pointer was used unsafely after it was verified against nullptr. Check lines: 2392, 2394.
↑ V1004 The 'pView' pointer was used unsafely after it was verified against nullptr. Check lines: 2506, 2508.