Quellcode-Bibliothek svdmrkv.cxx
Sprache: unbekannt
|
|
Columbo aufrufen.cxx Download desUnknown {[0] [0] [0]}Datei anzeigen
/* -*- 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 <svx/svdmrkv.hxx>
#include <svx/svdview.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdotable.hxx>
#include <svx/svdomedia.hxx>
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <rtl/strbuf.hxx>
#include <svx/svdoole2.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflgrit.hxx>
#include "gradtrns.hxx"
#include <svx/xflftrit.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <svx/svdundo.hxx>
#include <svx/svdopath.hxx>
#include <svx/scene3d.hxx>
#include <svx/svdovirt.hxx>
#include <sdr/overlay/overlayrollingrectangle.hxx>
#include <svx/sdr/overlay/overlaypolypolygon.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <svx/sdr/contact/objectcontact.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdr/overlay/overlayselection.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdrhittesthelper.hxx>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
#include <vcl/window.hxx>
#include <o3tl/string_view.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>
#include <sfx2/lokcomponenthelpers.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/objsh.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <drawinglayer/processor2d/textextractor2d.hxx>
#include <svl/cryptosign.hxx>
#include <array>
#include <com/sun/star/frame/XController.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <boost/property_tree/json_parser.hpp>
using namespace com::sun::star;
// Migrate Marking of Objects, Points and GluePoints
class ImplMarkingOverlay
{
// The OverlayObjects
sdr::overlay::OverlayObjectList maObjects;
// The remembered second position in logical coordinates
basegfx::B2DPoint maSecondPosition;
// A flag to remember if the action is for unmarking.
bool mbUnmarking : 1;
public:
ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStart Pos, bool bUnmarking);
// The OverlayObjects are cleared using the destructor of OverlayObjectList.
// That destructor calls clear() at the list which removes all objects from the
// OverlayManager and deletes them.
void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
bool IsUnmarking() const { return mbUnmarking; }
};
ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
: maSecondPosition(rStartPos),
mbUnmarking(bUnmarking)
{
if (comphelper::LibreOfficeKit::isActive())
return; // We do client-side object manipulation with the Kit API
for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
{
SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
if (xTargetOverlay.is())
{
std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(new sdr::overlay::OverlayRollingRectangleStriped(
rStartPos, rStartPos, false));
xTargetOverlay->add(*pNew);
maObjects.append(std::move(pNew));
}
}
}
void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
{
if(rNewPosition != maSecondPosition)
{
// apply to OverlayObjects
for(sal_uInt32 a(0); a < maObjects.count(); a++)
{
sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
rCandidate.setSecondPosition(rNewPosition);
}
// remember new position
maSecondPosition = rNewPosition;
}
}
class MarkingSelectionOverlay
{
sdr::overlay::OverlayObjectList maObjects;
public:
MarkingSelectionOverlay(const SdrPaintView& rView, basegfx::B2DRectangle const& rSelection)
{
if (comphelper::LibreOfficeKit::isActive())
return; // We do client-side object manipulation with the Kit API
for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
{
SdrPaintWindow* pPaintWindow = rView.GetPaintWindow(a);
const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pPaintWindow->GetOverlayManager();
if (xTargetOverlay.is())
{
basegfx::B2DPolyPolygon aPolyPoly(basegfx::utils::createPolygonFromRect(rSelection));
auto pNew = std::make_unique<sdr::overlay::OverlayPolyPolygon>(aPolyPoly, COL_GRAY, 0, COL_TRANSPARENT);
xTargetOverlay->add(*pNew);
maObjects.append(std::move(pNew));
}
}
}
};
class MarkingSubSelectionOverlay
{
sdr::overlay::OverlayObjectList maObjects;
public:
MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
{
if (comphelper::LibreOfficeKit::isActive())
return; // We do client-side object manipulation with the Kit API
for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
{
SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
if (xTargetOverlay.is())
{
const Color aHighlightColor = SvtOptionsDrawinglayer::getHilightColor();
std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
std::make_unique<sdr::overlay::OverlaySelection>(
sdr::overlay::OverlayType::Transparent,
aHighlightColor, std::vector(rSelections), false);
xTargetOverlay->add(*pNew);
maObjects.append(std::move(pNew));
}
}
}
};
SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
: SdrSnapView(rSdrModel, pOut)
, mpMarkedObj(nullptr)
, mpMarkedPV(nullptr)
, maHdlList(this)
, meDragMode(SdrDragMode::Move)
, meEditMode(SdrViewEditMode::Edit)
, meEditMode0(SdrViewEditMode::Edit)
, mbDesignMode(false)
, mbForceFrameHandles(false)
, mbPlusHdlAlways(false)
, mbInsPolyPoint(false)
, mbMarkedObjRectDirty(false)
, mbMrkPntDirty(false)
, mbMarkedPointsRectsDirty(false)
, mbMarkHandlesHidden(false)
, mbNegativeX(false)
{
BrkMarkObj();
BrkMarkPoints();
BrkMarkGluePoints();
StartListening(rSdrModel);
}
SdrMarkView::~SdrMarkView()
{
// Migrate selections
BrkMarkObj();
BrkMarkPoints();
BrkMarkGluePoints();
}
void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{
if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
{
const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
SdrHintKind eKind=pSdrHint->GetKind();
if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
{
mbMarkedObjRectDirty=true;
mbMarkedPointsRectsDirty=true;
}
}
SdrSnapView::Notify(rBC,rHint);
}
void SdrMarkView::ModelHasChanged()
{
SdrPaintView::ModelHasChanged();
GetMarkedObjectListWriteAccess().SetNameDirty();
mbMarkedObjRectDirty=true;
mbMarkedPointsRectsDirty=true;
// Example: Obj is selected and maMarkedObjectList is sorted.
// In another View 2, the ObjOrder is changed (e. g. MovToTop())
// Then we need to re-sort MarkList.
GetMarkedObjectListWriteAccess().SetUnsorted();
const SdrMarkList& rMarkList = GetMarkedObjectList();
rMarkList.ForceSort();
mbMrkPntDirty=true;
UndirtyMrkPnt();
SdrView* pV=static_cast<SdrView*>(this);
if (!pV->IsDragObj() && !pV->IsInsObjPoint()) {
AdjustMarkHdl();
}
if (comphelper::LibreOfficeKit::isActive())
modelHasChangedLOKit();
}
void SdrMarkView::modelHasChangedLOKit()
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if (rMarkList.GetMarkCount() <= 0)
return;
//TODO: Is MarkedObjRect valid at this point?
tools::Rectangle aSelection(GetMarkedObjRect());
tools::Rectangle* pResultSelection;
if (aSelection.IsEmpty())
pResultSelection = nullptr;
else
{
sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
if (nTotalPaintWindows == 1)
{
const OutputDevice* pOut = this->GetFirstOutputDevice();
const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
if (pWin && pWin->IsChart())
{
if (SfxViewShell* pViewShell = GetSfxViewShell())
{
const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
{
Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
}
}
}
}
// In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
if (mpMarkedPV)
{
if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
{
if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
}
}
pResultSelection = &aSelection;
if (mbNegativeX)
{
// Convert to positive X doc-coordinates
tools::Long nTmp = aSelection.Left();
aSelection.SetLeft(-aSelection.Right());
aSelection.SetRight(-nTmp);
}
}
if (SfxViewShell* pViewShell = GetSfxViewShell())
SfxLokHelper::notifyInvalidation(pViewShell, pResultSelection);
}
bool SdrMarkView::IsAction() const
{
return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
}
void SdrMarkView::MovAction(const Point& rPnt)
{
SdrSnapView::MovAction(rPnt);
if(IsMarkObj())
{
MovMarkObj(rPnt);
}
else if(IsMarkPoints())
{
MovMarkPoints(rPnt);
}
else if(IsMarkGluePoints())
{
MovMarkGluePoints(rPnt);
}
}
void SdrMarkView::EndAction()
{
if(IsMarkObj())
{
EndMarkObj();
}
else if(IsMarkPoints())
{
EndMarkPoints();
}
else if(IsMarkGluePoints())
{
EndMarkGluePoints();
}
SdrSnapView::EndAction();
}
void SdrMarkView::BckAction()
{
SdrSnapView::BckAction();
BrkMarkObj();
BrkMarkPoints();
BrkMarkGluePoints();
}
void SdrMarkView::BrkAction()
{
SdrSnapView::BrkAction();
BrkMarkObj();
BrkMarkPoints();
BrkMarkGluePoints();
}
void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
{
if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
{
rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
}
else
{
SdrSnapView::TakeActionRect(rRect);
}
}
void SdrMarkView::ClearPageView()
{
UnmarkAllObj();
SdrSnapView::ClearPageView();
}
void SdrMarkView::HideSdrPage()
{
bool bMrkChg(false);
SdrPageView* pPageView = GetSdrPageView();
if (pPageView)
{
// break all creation actions when hiding page (#75081#)
BrkAction();
// Discard all selections on this page
bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
}
SdrSnapView::HideSdrPage();
if(bMrkChg)
{
MarkListHasChanged();
AdjustMarkHdl();
}
}
void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
{
BrkAction();
DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");
basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
maDragStat.Reset(rPnt);
maDragStat.NextPoint();
maDragStat.SetMinMove(mnMinMovLog);
}
void SdrMarkView::MovMarkObj(const Point& rPnt)
{
if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
{
maDragStat.NextMove(rPnt);
DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
mpMarkObjOverlay->SetSecondPosition(aNewPos);
}
}
bool SdrMarkView::EndMarkObj()
{
bool bRetval(false);
if(IsMarkObj())
{
if(maDragStat.IsMinMoved())
{
tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
aRect.Normalize();
MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
bRetval = true;
}
// cleanup
BrkMarkObj();
}
return bRetval;
}
void SdrMarkView::BrkMarkObj()
{
if(IsMarkObj())
{
DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
mpMarkObjOverlay.reset();
}
}
bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
{
if(HasMarkablePoints())
{
BrkAction();
DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
maDragStat.Reset(rPnt);
maDragStat.NextPoint();
maDragStat.SetMinMove(mnMinMovLog);
return true;
}
return false;
}
void SdrMarkView::MovMarkPoints(const Point& rPnt)
{
if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
{
maDragStat.NextMove(rPnt);
DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
mpMarkPointsOverlay->SetSecondPosition(aNewPos);
}
}
bool SdrMarkView::EndMarkPoints()
{
bool bRetval(false);
if(IsMarkPoints())
{
if(maDragStat.IsMinMoved())
{
tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
aRect.Normalize();
MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());
bRetval = true;
}
// cleanup
BrkMarkPoints();
}
return bRetval;
}
void SdrMarkView::BrkMarkPoints()
{
if(IsMarkPoints())
{
DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
mpMarkPointsOverlay.reset();
}
}
bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
{
if(HasMarkableGluePoints())
{
BrkAction();
DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");
basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
maDragStat.Reset(rPnt);
maDragStat.NextPoint();
maDragStat.SetMinMove(mnMinMovLog);
return true;
}
return false;
}
void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
{
if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
{
maDragStat.NextMove(rPnt);
DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
}
}
void SdrMarkView::EndMarkGluePoints()
{
if(IsMarkGluePoints())
{
if(maDragStat.IsMinMoved())
{
tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
aRect.Normalize();
MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
}
// cleanup
BrkMarkGluePoints();
}
}
void SdrMarkView::BrkMarkGluePoints()
{
if(IsMarkGluePoints())
{
DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
mpMarkGluePointsOverlay.reset();
}
}
bool SdrMarkView::MarkableObjectsExceed( int n ) const
{
SdrPageView* pPV = GetSdrPageView();
if (!pPV)
return false;
SdrObjList* pOL=pPV->GetObjList();
for (const rtl::Reference<SdrObject>& pObj : *pOL)
if (IsObjMarkable(pObj.get(),pPV) && --n<0)
return true;
return false;
}
void SdrMarkView::hideMarkHandles()
{
if(!mbMarkHandlesHidden)
{
mbMarkHandlesHidden = true;
AdjustMarkHdl();
}
}
void SdrMarkView::showMarkHandles()
{
if(mbMarkHandlesHidden)
{
mbMarkHandlesHidden = false;
AdjustMarkHdl();
}
}
bool SdrMarkView::ImpIsFrameHandles() const
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nMarkCount=rMarkList.GetMarkCount();
bool bFrmHdl=nMarkCount>static_cast<size_t>(mnFrameHandlesLimit) || mbForceFrameHandles;
bool bStdDrag=meDragMode==SdrDragMode::Move;
if (nMarkCount==1 && bStdDrag && bFrmHdl)
{
const SdrObject* pObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
if (pObj && pObj->GetObjInventor()==SdrInventor::Default)
{
SdrObjKind nIdent=pObj->GetObjIdentifier();
if (nIdent==SdrObjKind::Line || nIdent==SdrObjKind::Edge || nIdent==SdrObjKind::Caption || nIdent==SdrObjKind::Measure || nIdent==SdrObjKind::CustomShape || nIdent==SdrObjKind::Table )
{
bFrmHdl=false;
}
}
}
if (!bStdDrag && !bFrmHdl) {
// all other drag modes only with FrameHandles
bFrmHdl=true;
if (meDragMode==SdrDragMode::Rotate) {
// when rotating, use ObjOwn drag, if there's at least 1 PolyObj
for (size_t nMarkNum=0; nMarkNum<nMarkCount && bFrmHdl; ++nMarkNum) {
const SdrMark* pM=rMarkList.GetMark(nMarkNum);
const SdrObject* pObj=pM->GetMarkedSdrObj();
bFrmHdl=!pObj->IsPolyObj();
}
}
}
if (!bFrmHdl) {
// FrameHandles, if at least 1 Obj can't do SpecialDrag
for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bFrmHdl; ++nMarkNum) {
const SdrMark* pM=rMarkList.GetMark(nMarkNum);
const SdrObject* pObj=pM->GetMarkedSdrObj();
bFrmHdl=!pObj->hasSpecialDrag();
}
}
// no FrameHdl for crop
if(bFrmHdl && SdrDragMode::Crop == meDragMode)
{
bFrmHdl = false;
}
return bFrmHdl;
}
namespace
{
std::u16string_view lcl_getDragMethodServiceName( std::u16string_view rCID )
{
std::u16string_view aRet;
size_t nIndexStart = rCID.find( u"DragMethod=" );
if( nIndexStart != std::u16string_view::npos )
{
nIndexStart = rCID.find( '=', nIndexStart );
if( nIndexStart != std::u16string_view::npos )
{
nIndexStart++;
size_t nNextSlash = rCID.find( '/', nIndexStart );
if( nNextSlash != std::u16string_view::npos )
{
sal_Int32 nIndexEnd = nNextSlash;
size_t nNextColon = rCID.find( ':', nIndexStart );
if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
nIndexEnd = nNextColon;
aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
}
}
}
return aRet;
}
std::u16string_view lcl_getDragParameterString( std::u16string_view rCID )
{
std::u16string_view aRet;
size_t nIndexStart = rCID.find( u"DragParameter=" );
if( nIndexStart != std::u16string_view::npos )
{
nIndexStart = rCID.find( '=', nIndexStart );
if( nIndexStart != std::u16string_view::npos )
{
nIndexStart++;
size_t nNextSlash = rCID.find( '/', nIndexStart );
if( nNextSlash != std::u16string_view::npos )
{
sal_Int32 nIndexEnd = nNextSlash;
size_t nNextColon = rCID.find( ':', nIndexStart );
if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
nIndexEnd = nNextColon;
aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
}
}
}
return aRet;
}
} // anonymous namespace
bool SdrMarkView::dumpGluePointsToJSON(boost::property_tree::ptree& rTree)
{
bool result = false;
tools::Long nSignX = mbNegativeX ? -1 : 1;
if (OutputDevice* pOutDev = mpMarkedPV ? mpMarkedPV->GetView().GetFirstOutputDevice() : nullptr)
{
bool bConvertUnit = false;
if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
bConvertUnit = true;
const SdrObjList* pOL = mpMarkedPV->GetObjList();
if (!pOL)
return false;
boost::property_tree::ptree elements;
const SdrMarkList& rMarkList = GetMarkedObjectList();
for (const rtl::Reference<SdrObject>& pObj : *pOL)
{
if (!pObj)
continue;
if (pObj == rMarkList.GetMark(0)->GetMarkedSdrObj())
continue;
const SdrGluePointList* pGPL = pObj->GetGluePointList();
bool VertexObject = !(pGPL && pGPL->GetCount());
const size_t count = !VertexObject ? pGPL->GetCount() : 4;
boost::property_tree::ptree object;
boost::property_tree::ptree points;
for (size_t i = 0; i < count; ++i)
{
boost::property_tree::ptree node;
boost::property_tree::ptree point;
const SdrGluePoint aGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
Point rPoint = aGP.GetAbsolutePos(*pObj);
if (bConvertUnit)
{
rPoint = o3tl::convert(rPoint, o3tl::Length::mm100, o3tl::Length::twip);
}
point.put("x", nSignX * rPoint.getX());
point.put("y", rPoint.getY());
node.add_child("point", point);
points.push_back(std::make_pair("", node));
}
basegfx::B2DVector aGridOffset(0.0, 0.0);
Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
{
Point p(aGridOffset.getX(), aGridOffset.getY());
if (bConvertUnit)
{
p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
}
boost::property_tree::ptree gridOffset;
gridOffset.put("x", nSignX * p.getX());
gridOffset.put("y", p.getY());
object.add_child("gridoffset", gridOffset);
}
object.put("ordnum", pObj->GetOrdNum());
object.add_child("gluepoints", points);
elements.push_back(std::make_pair("", object));
result = true;
}
rTree.add_child("shapes", elements);
}
return result;
}
namespace
{
class TextBoundsExtractor final : public drawinglayer::processor2d::TextExtractor2D
{
private:
basegfx::B2DRange maTextRange;
void processTextPrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override
{
maTextRange.expand(rCandidate.getB2DRange(getViewInformation2D()));
}
public:
explicit TextBoundsExtractor(const drawinglayer::geometry::ViewInformation2D& rViewInformation)
: drawinglayer::processor2d::TextExtractor2D(rViewInformation)
{
}
const basegfx::B2DRange & getTextBounds(const sdr::contact::ViewObjectContact &rVOC, const sdr::contact::DisplayInfo &raDisplayInfo)
{
this->process(rVOC.getPrimitive2DSequence(raDisplayInfo));
return maTextRange;
}
};
}
OString SdrMarkView::CreateInnerTextRectString() const
{
if (!mpMarkedObj)
return OString();
SdrPageView* pPageView = GetSdrPageView();
const sdr::contact::ViewObjectContact& rVOC = mpMarkedObj->GetViewContact().GetViewObjectContact(
pPageView->GetPageWindow(0)->GetObjectContact());
sdr::contact::DisplayInfo aDisplayInfo;
TextBoundsExtractor aTextBoundsExtractor(rVOC.GetObjectContact().getViewInformation2D());
basegfx::B2DRange aRange = aTextBoundsExtractor.getTextBounds(rVOC, aDisplayInfo);
if (!aRange.isEmpty()) {
tools::Rectangle rect(aRange.getMinX(), aRange.getMinY(), aRange.getMaxX(), aRange.getMaxY());
tools::Rectangle aRangeTWIP = o3tl::convert(rect, o3tl::Length::mm100, o3tl::Length::twip);
OString innerTextInfo = "\"innerTextRect\":[" +
OString::number(aRangeTWIP.getX()) + "," +
OString::number(aRangeTWIP.getY()) + "," +
OString::number(aRangeTWIP.GetWidth()) + "," +
OString::number(aRangeTWIP.GetHeight()) + "]";
return innerTextInfo;
}
return OString();
}
void SdrMarkView::SetInnerTextAreaForLOKit() const
{
if (!comphelper::LibreOfficeKit::isActive())
return;
SfxViewShell* pViewShell = GetSfxViewShell();
OString sRectString = CreateInnerTextRectString();
if (pViewShell && !sRectString.isEmpty())
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SHAPE_INNER_TEXT, sRectString);
}
void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
{
SfxViewShell* pViewShell = GetSfxViewShell();
tools::Rectangle aSelection(rRect);
tools::Long nSignX = mbNegativeX ? -1 : 1;
bool bIsChart = false;
Point addLogicOffset(0, 0);
bool convertMapMode = false;
if (!rRect.IsEmpty())
{
sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
if (nTotalPaintWindows == 1)
{
const OutputDevice* pOut = this->GetFirstOutputDevice();
const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
if (pWin && pWin->IsChart())
{
bIsChart = true;
const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
{
Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
if (mbNegativeX && AllSettings::GetLayoutRTL())
{
// mbNegativeX is set only for Calc in RTL mode.
// If global RTL flag is set, vcl-window X offset of chart window is
// mirrored w.r.t parent window rectangle. This needs to be reverted.
aOffsetPx.setX(pViewShellWindow->GetOutOffXPixel() + pViewShellWindow->GetSizePixel().Width()
- pWin->GetOutOffXPixel() - pWin->GetSizePixel().Width());
}
Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
addLogicOffset = aLogicOffset;
aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
}
}
}
}
if (!aSelection.IsEmpty())
{
// In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
if (mpMarkedPV)
{
if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
{
if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
{
aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
convertMapMode = true;
}
}
}
// hide the text selection too
if (pViewShell)
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
}
{
OStringBuffer aExtraInfo;
OString sSelectionText;
OString sSelectionTextView;
boost::property_tree::ptree aTableJsonTree;
boost::property_tree::ptree aGluePointsTree;
const bool bMediaObj = (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Media);
bool bTableSelection = false;
bool bConnectorSelection = false;
if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Table)
{
auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
}
if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Edge)
{
bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
}
SdrPageView* pPageView = GetSdrPageView();
const SdrMarkList& rMarkList = GetMarkedObjectList();
if (rMarkList.GetMarkCount())
{
SdrMark* pM = rMarkList.GetMark(0);
SdrObject* pO = pM->GetMarkedSdrObj();
Degree100 nRotAngle = pO->GetRotateAngle();
// true if we are dealing with a RotGrfFlyFrame
// (SwVirtFlyDrawObj with a SwGrfNode)
bool bWriterGraphic = pO->HasLimitedRotation();
OString handleArrayStr;
aExtraInfo.append("{\"id\":\""
+ OString::number(reinterpret_cast<sal_IntPtr>(pO))
+ "\",\"type\":"
+ OString::number(static_cast<sal_Int32>(pO->GetObjIdentifier()))
+ ",\"OrdNum\":" + OString::number(pO->GetOrdNum())
);
if (mpMarkedObj && !pOtherShell)
{
OString innerTextInfo = CreateInnerTextRectString();
if (!innerTextInfo.isEmpty())
aExtraInfo.append("," + innerTextInfo);
}
// In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
// In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
// SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
// but the rotation is not applied to the LogicRect. Therefore,
// what we calculate in online does not match with the core in case of the rotation.
// Here we can send the correct gridOffset in the selection callback, this way
// whether the shape is rotated or not, we will always have the correct gridOffset
// Note that the gridOffset is calculated from the first selected obj
basegfx::B2DVector aGridOffset(0.0, 0.0);
if(getPossibleGridOffsetForSdrObject(aGridOffset, rMarkList.GetMark(0)->GetMarkedSdrObj(), pPageView))
{
Point p(aGridOffset.getX(), aGridOffset.getY());
if (convertMapMode)
p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
aExtraInfo.append(",\"gridOffsetX\":"
+ OString::number(nSignX * p.getX())
+ ",\"gridOffsetY\":"
+ OString::number(p.getY()));
}
if (meDragMode == SdrDragMode::Crop)
aExtraInfo.append(", \"isCropMode\": true");
if (bWriterGraphic)
{
aExtraInfo.append(", \"isWriterGraphic\": true");
}
else if (bIsChart)
{
LokChartHelper aChartHelper(pViewShell);
css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
if (xSelectionSupplier.is())
{
uno::Any aSel = xSelectionSupplier->getSelection();
OUString aValue;
if (aSel >>= aValue)
{
OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
const std::vector<OString> aProps{"Draggable"_ostr, "Resizable"_ostr, "Rotatable"_ostr};
for (const auto& rProp: aProps)
{
sal_Int32 nPos = aObjectCID.indexOf(rProp);
if (nPos == -1) continue;
nPos += rProp.getLength() + 1; // '='
if (aExtraInfo.getLength() > 2) // != "{ "
aExtraInfo.append(", ");
aExtraInfo.append("\"is" + rProp + "\": "
+ OString::boolean(aObjectCID[nPos] == '1'));
}
std::u16string_view sDragMethod = lcl_getDragMethodServiceName(aValue);
if (sDragMethod == u"PieSegmentDragging")
{
// old initial offset inside the CID returned by xSelectionSupplier->getSelection()
// after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
aValue = pO->GetName();
std::u16string_view sDragParameters = lcl_getDragParameterString(aValue);
if (!sDragParameters.empty())
{
aExtraInfo.append(", \"dragInfo\": { "
"\"dragMethod\": \""
+ OUString(sDragMethod).toUtf8()
+ "\"");
sal_Int32 nStartIndex = 0;
std::array<int, 5> aDragParameters;
for (auto& rParam : aDragParameters)
{
std::u16string_view sParam = o3tl::getToken(sDragParameters, 0, ',', nStartIndex);
if (sParam.empty())
break;
rParam = o3tl::toInt32(sParam);
}
// initial offset in %
if (aDragParameters[0] < 0)
aDragParameters[0] = 0;
else if (aDragParameters[0] > 100)
aDragParameters[0] = 100;
aExtraInfo.append(", \"initialOffset\": "
+ OString::number(static_cast<sal_Int32>(aDragParameters[0])));
// drag direction constraint
Point aMinPos(aDragParameters[1], aDragParameters[2]);
Point aMaxPos(aDragParameters[3], aDragParameters[4]);
Point aDragDirection = aMaxPos - aMinPos;
aDragDirection = o3tl::convert(aDragDirection, o3tl::Length::mm100, o3tl::Length::twip);
aExtraInfo.append(", \"dragDirection\": ["
+ aDragDirection.toString()
+ "]");
// polygon approximating the pie segment or donut segment
if (pViewShell && pO->GetObjIdentifier() == SdrObjKind::PathFill)
{
const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
if (aPolyPolygon.count() == 1)
{
const basegfx::B2DPolygon& aPolygon = aPolyPolygon.getB2DPolygon(0);
if (sal_uInt32 nPolySize = aPolygon.count())
{
const OutputDevice* pOut = this->GetFirstOutputDevice();
const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
{
// in the following code escaping sequences used inside raw literal strings
// are for making them understandable by the JSON parser
Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
OString sPolygonElem("<polygon points=\\\""_ostr);
for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
{
const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
Point aPoint(aB2Point.getX(), aB2Point.getY());
aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
if (mbNegativeX)
aPoint.setX(-aPoint.X());
if (nIndex > 0)
sPolygonElem += " ";
sPolygonElem += aPoint.toString();
}
sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";
OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
OString::number(aSelection.GetWidth() / 100.0) +
R"elem(mm\" height=\")elem" +
OString::number(aSelection.GetHeight() / 100.0) +
R"elem(mm\" viewBox=\")elem" +
aSelection.toString() +
R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";
aExtraInfo.append(", \"svg\": \""
+ sSVGElem
+ "\\n "
+ sPolygonElem
+ "\\n</svg>"
"\""); // svg
}
}
}
}
aExtraInfo.append("}"); // dragInfo
}
}
}
}
}
else
{
SfxObjectShell* pObjectShell = pViewShell ? pViewShell->GetObjectShell() : nullptr;
if (pObjectShell && pObjectShell->IsSignPDF() && pViewShell && pViewShell->GetSignPDFCertificate().Is())
{
// Expose the info that this is the special signature widget that is OK to
// move/resize.
aExtraInfo.append(", \"isSignature\": true");
}
}
if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
{
boost::property_tree::ptree responseJSON;
boost::property_tree::ptree others;
boost::property_tree::ptree anchor;
boost::property_tree::ptree rectangle;
boost::property_tree::ptree poly;
boost::property_tree::ptree custom;
boost::property_tree::ptree nodes;
for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
{
SdrHdl *pHdl = maHdlList.GetHdl(i);
boost::property_tree::ptree child;
boost::property_tree::ptree point;
sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
child.put("id", pHdl->GetObjHdlNum());
child.put("kind", kind);
child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
Point pHdlPos = pHdl->GetPos();
pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
if (convertMapMode)
{
pHdlPos = o3tl::convert(pHdlPos, o3tl::Length::mm100, o3tl::Length::twip);
}
point.put("x", pHdlPos.getX());
point.put("y", pHdlPos.getY());
child.add_child("point", point);
const auto node = std::make_pair("", child);
boost::property_tree::ptree* selectedNode = nullptr;
if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
{
selectedNode = &rectangle;
}
else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
{
selectedNode = &poly;
}
else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
{
selectedNode = &custom;
}
else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
{
if (getSdrModelFromSdrView().IsWriter())
selectedNode = &anchor;
else
// put it to others as we don't render them except in writer
selectedNode = &others;
}
else
{
selectedNode = &others;
}
std::string sKind = std::to_string(kind);
boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
if (!kindNode)
{
boost::property_tree::ptree newChild;
newChild.push_back(node);
selectedNode->add_child(sKind.c_str(), newChild);
}
else
kindNode.get().push_back(node);
}
nodes.add_child("rectangle", rectangle);
nodes.add_child("poly", poly);
nodes.add_child("custom", custom);
nodes.add_child("anchor", anchor);
nodes.add_child("others", others);
responseJSON.add_child("kinds", nodes);
std::stringstream aStream;
boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
handleArrayStr = ", \"handles\":"_ostr;
handleArrayStr = handleArrayStr + aStream.str().c_str();
if (bConnectorSelection)
{
aStream.str("");
boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
handleArrayStr = handleArrayStr + ", \"GluePoints\":";
handleArrayStr = handleArrayStr + aStream.str().c_str();
}
}
if (mbNegativeX)
{
tools::Rectangle aNegatedRect(aSelection);
aNegatedRect.SetLeft(-aNegatedRect.Left());
aNegatedRect.SetRight(-aNegatedRect.Right());
aNegatedRect.Normalize();
sSelectionText = aNegatedRect.toString() +
", " + OString::number(nRotAngle.get());
}
else
{
sSelectionText = aSelection.toString() +
", " + OString::number(nRotAngle.get());
}
if (!aExtraInfo.isEmpty())
{
sSelectionTextView = sSelectionText + ", " + aExtraInfo + "}";
if (bMediaObj && pOtherShell == nullptr)
{
// Add the URL only if we have a Media Object and
// we are the selecting view.
SdrMediaObj* mediaObj = dynamic_cast<SdrMediaObj*>(mpMarkedObj);
if (mediaObj)
aExtraInfo.append(", \"url\": \"" + mediaObj->getTempURL().toUtf8() + "\"");
}
SdrPage *pPage = pPageView ? pPageView->GetPage(): nullptr;
if (pPage && getSdrModelFromSdrView().IsImpress())
{
// Send all objects' rectangles along with the selected object's information.
// Other rectangles can be used for aligning the selected object referencing the others.
// Replace curly braces with empty string in order to merge it with the resulting string.
OString objectRectangles = SdrObjList::GetObjectRectangles(*pPage).replaceAll("{"_ostr, ""_ostr).replaceAll("}"_ostr, ""_ostr);
aExtraInfo.append(", \"ObjectRectangles\": "_ostr + objectRectangles);
}
aExtraInfo.append(handleArrayStr
+ "}");
sSelectionText += ", " + aExtraInfo;
}
}
if (sSelectionText.isEmpty())
{
sSelectionText = "EMPTY"_ostr;
sSelectionTextView = "EMPTY"_ostr;
if (!pOtherShell && pViewShell)
pViewShell->NotifyOtherViews(LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, OString());
}
if (bTableSelection)
{
boost::property_tree::ptree aTableRectangle;
aTableRectangle.put("x", aSelection.Left());
aTableRectangle.put("y", aSelection.Top());
aTableRectangle.put("width", aSelection.GetWidth());
aTableRectangle.put("height", aSelection.GetHeight());
aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));
std::stringstream aStream;
boost::property_tree::write_json(aStream, aTableJsonTree);
if (pViewShell)
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, OString(aStream.str()));
}
else if (!getSdrModelFromSdrView().IsWriter() && pViewShell)
{
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}"_ostr);
}
if (pOtherShell)
{
// Another shell wants to know about our existing
// selection.
if (pViewShell != pOtherShell)
SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
}
else if (pViewShell)
{
// We have a new selection, so both pViewShell and the
// other views want to know about it.
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText);
SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
}
}
}
void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
{
// remember old focus handle values to search for it again
const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
bool bSaveOldFocus(false);
sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
SdrHdlKind eSaveKind(SdrHdlKind::Move);
SdrObject* pSaveObj = nullptr;
mpMarkingSubSelectionOverlay.reset();
mpMarkingSelectionOverlay.reset();
if(pSaveOldFocusHdl
&& pSaveOldFocusHdl->GetObj()
&& dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
&& (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
{
bSaveOldFocus = true;
nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
nSavePointNum = pSaveOldFocusHdl->GetPointNum();
pSaveObj = pSaveOldFocusHdl->GetObj();
eSaveKind = pSaveOldFocusHdl->GetKind();
}
// delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
maHdlList.Clear();
maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
mpMarkedObj=nullptr;
mpMarkedPV=nullptr;
// are handles enabled at all? Create only then
if(areMarkHandlesHidden())
return;
// There can be multiple mark views, but we're only interested in the one that has a window associated.
const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;
const SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nMarkCount=rMarkList.GetMarkCount();
bool bStdDrag=meDragMode==SdrDragMode::Move;
bool bSingleTextObjMark=false;
bool bLimitedRotation(false);
if (nMarkCount==1)
{
mpMarkedObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
if(nullptr != mpMarkedObj)
{
bSingleTextObjMark =
DynCastSdrTextObj( mpMarkedObj) != nullptr &&
static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
// RotGrfFlyFrame: we may have limited rotation
bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
}
}
bool bFrmHdl=ImpIsFrameHandles();
if (nMarkCount>0)
{
mpMarkedPV=rMarkList.GetMark(0)->GetPageView();
for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
{
const SdrMark* pM=rMarkList.GetMark(nMarkNum);
if (mpMarkedPV!=pM->GetPageView())
{
mpMarkedPV=nullptr;
}
}
}
tools::Rectangle aRect(GetMarkedObjRect());
if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Annotation)
{
basegfx::B2DRectangle aB2DRect(aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom());
mpMarkingSelectionOverlay = std::make_unique<MarkingSelectionOverlay>(*this, aB2DRect);
return;
}
SfxViewShell* pViewShell = GetSfxViewShell();
// check if text edit or ole is active and handles need to be suppressed. This may be the case
// when a single object is selected
// Using a strict return statement is okay here; no handles means *no* handles.
if (mpMarkedObj)
{
// formerly #i33755#: If TextEdit is active the EditEngine will directly paint
// to the window, so suppress Overlay and handles completely; a text frame for
// the active text edit will be painted by the repaint mechanism in
// SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
// in the future
// Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
if(static_cast<SdrView*>(this)->IsTextEdit())
{
const SdrTextObj* pSdrTextObj = DynCastSdrTextObj(mpMarkedObj);
if (pSdrTextObj && pSdrTextObj->IsInEditMode())
{
if (!bTiledRendering)
return;
}
}
// formerly #i118524#: if inplace activated OLE is selected, suppress handles
const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);
if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
{
return;
}
if (!maSubSelectionList.empty())
{
mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
}
}
if (bFrmHdl)
{
if(!aRect.IsEmpty())
{
// otherwise nothing is found
const size_t nSiz0(maHdlList.GetHdlCount());
if( bSingleTextObjMark )
{
mpMarkedObj->AddToHdlList(maHdlList);
}
else
{
const bool bWdt0(aRect.Left() == aRect.Right());
const bool bHgt0(aRect.Top() == aRect.Bottom());
if (bWdt0 && bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
}
else if (!bStdDrag && (bWdt0 || bHgt0))
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
}
else
{
if (!bWdt0 && !bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
}
if (!bLimitedRotation && !bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
}
if (!bWdt0 && !bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
}
if (!bLimitedRotation && !bWdt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
}
if (!bWdt0 && !bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
}
if (!bLimitedRotation && !bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
}
if (!bWdt0 && !bHgt0)
{
maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
}
}
}
// Diagram selection visualization support
// Caution: CppunitTest_sd_tiledrendering shows that mpMarkedObj *can* actually be nullptr (!)
if(nullptr != mpMarkedObj && mpMarkedObj->isDiagram())
{
mpMarkedObj->AddToHdlList(maHdlList);
}
const size_t nSiz1(maHdlList.GetHdlCount());
// moved setting the missing parameters at SdrHdl here from the
// single loop above (bSingleTextObjMark), this was missing all
// the time. Setting SdrObject is now required to correctly get
// the View-Dependent evtl. GridOffset adapted
for (size_t i=nSiz0; i<nSiz1; ++i)
{
SdrHdl* pHdl=maHdlList.GetHdl(i);
pHdl->SetObj(mpMarkedObj);
pHdl->SetPageView(mpMarkedPV);
pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
}
}
}
else
{
bool bDone(false);
// moved crop handling to non-frame part and the handle creation to SdrGrafObj
if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
{
// Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
// behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
// writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
const size_t nSiz0(maHdlList.GetHdlCount());
mpMarkedObj->addCropHandles(maHdlList);
const size_t nSiz1(maHdlList.GetHdlCount());
// Was missing: Set infos at SdrCropHdl
for (size_t i=nSiz0; i<nSiz1; ++i)
{
SdrHdl* pHdl=maHdlList.GetHdl(i);
pHdl->SetObj(mpMarkedObj);
pHdl->SetPageView(mpMarkedPV);
pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
}
bDone = true;
}
if(!bDone)
{
for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
{
const SdrMark* pM=rMarkList.GetMark(nMarkNum);
SdrObject* pObj=pM->GetMarkedSdrObj();
SdrPageView* pPV=pM->GetPageView();
const size_t nSiz0=maHdlList.GetHdlCount();
pObj->AddToHdlList(maHdlList);
const size_t nSiz1=maHdlList.GetHdlCount();
bool bPoly=pObj->IsPolyObj();
const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
for (size_t i=nSiz0; i<nSiz1; ++i)
{
SdrHdl* pHdl=maHdlList.GetHdl(i);
pHdl->SetObj(pObj);
pHdl->SetPageView(pPV);
pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
if (bPoly)
{
bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
pHdl->SetSelected(bSelected);
if (mbPlusHdlAlways || bSelected)
{
SdrHdlList plusList(nullptr);
pObj->AddToPlusHdlList(plusList, *pHdl);
sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
{
SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
pPlusHdl->SetObj(pObj);
pPlusHdl->SetPageView(pPV);
pPlusHdl->SetPlusHdl(true);
}
plusList.MoveTo(maHdlList);
}
}
}
}
}
}
// GluePoint handles
for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
{
const SdrMark* pM=rMarkList.GetMark(nMarkNum);
SdrObject* pObj=pM->GetMarkedSdrObj();
const SdrGluePointList* pGPL=pObj->GetGluePointList();
if (!pGPL)
continue;
SdrPageView* pPV=pM->GetPageView();
const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
for (sal_uInt16 nId : rMrkGlue)
{
//nNum changed to nNumGP because already used in for loop
sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
{
const SdrGluePoint& rGP=(*pGPL)[nNumGP];
Point aPos(rGP.GetAbsolutePos(*pObj));
std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
pGlueHdl->SetObj(pObj);
pGlueHdl->SetPageView(pPV);
pGlueHdl->SetObjHdlNum(nId);
maHdlList.AddHdl(std::move(pGlueHdl));
}
}
}
// rotation point/axis of reflection
if(!bLimitedRotation)
{
AddDragModeHdl(meDragMode);
}
// sort handles
maHdlList.Sort();
// add custom handles (used by other apps, e.g. AnchorPos)
AddCustomHdl();
// moved it here to access all the handles for callback.
if (bTiledRendering && pViewShell)
{
SetMarkHandlesForLOKit(aRect, pOtherShell);
}
// try to restore focus handle index from remembered values
if(!bSaveOldFocus)
return;
for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
{
SdrHdl* pCandidate = maHdlList.GetHdl(a);
if(pCandidate->GetObj()
&& pCandidate->GetObj() == pSaveObj
&& pCandidate->GetKind() == eSaveKind
&& pCandidate->GetPolyNum() == nSavePolyNum
&& pCandidate->GetPointNum() == nSavePointNum)
{
maHdlList.SetFocusHdl(pCandidate);
break;
}
}
}
void SdrMarkView::AddCustomHdl()
{
// add custom handles (used by other apps, e.g. AnchorPos)
}
void SdrMarkView::SetDragMode(SdrDragMode eMode)
{
SdrDragMode eMode0=meDragMode;
meDragMode=eMode;
if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
if (meDragMode!=eMode0) {
ForceRefToMarked();
SetMarkHandles(nullptr);
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if (rMarkList.GetMarkCount() != 0) MarkListHasChanged();
}
}
}
void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
{
switch(eMode)
{
case SdrDragMode::Rotate:
{
// add rotation center
maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
break;
}
case SdrDragMode::Mirror:
{
// add axis of reflection
std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));
pHdl1->SetObjHdlNum(1); // for sorting
pHdl2->SetObjHdlNum(2); // for sorting
pHdl3->SetObjHdlNum(3); // for sorting
maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
maHdlList.AddHdl(std::move(pHdl2));
maHdlList.AddHdl(std::move(pHdl3));
break;
}
case SdrDragMode::Transparence:
{
// add interactive transparency handle
const SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nMarkCount = rMarkList.GetMarkCount();
if(nMarkCount == 1)
{
SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
SdrModel& rModel = GetModel();
const SfxItemSet& rSet = pObj->GetMergedItemSet();
if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
{
// add this item, it's not yet there
XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
basegfx::BGradient aGrad = aNewItem.GetGradientValue();
aNewItem.SetEnabled(true);
aGrad.SetStartIntens(100);
aGrad.SetEndIntens(100);
aNewItem.SetGradientValue(aGrad);
// add undo to allow user to take back this step
if (rModel.IsUndoEnabled())
--> --------------------
--> maximum size reached
--> --------------------
[ Verzeichnis aufwärts0.68unsichere Verbindung
]
|
2026-04-04
|