Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / svdraw / svdmrkv.cxx
blobd815c99bcc069f191fea50297bc5bf548663378c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <svx/svdmrkv.hxx>
22 #include <svx/svdview.hxx>
23 #include <svx/svdpagv.hxx>
24 #include <svx/svdpage.hxx>
25 #include <svx/svdotable.hxx>
26 #include <svx/svdomedia.hxx>
28 #include <osl/diagnose.h>
29 #include <osl/thread.h>
30 #include <rtl/strbuf.hxx>
31 #include <svx/svdoole2.hxx>
32 #include <svx/xfillit0.hxx>
33 #include <svx/xflgrit.hxx>
34 #include "gradtrns.hxx"
35 #include <svx/xflftrit.hxx>
36 #include <svx/dialmgr.hxx>
37 #include <svx/strings.hrc>
38 #include <svx/svdundo.hxx>
39 #include <svx/svdopath.hxx>
40 #include <svx/scene3d.hxx>
41 #include <svx/svdovirt.hxx>
42 #include <sdr/overlay/overlayrollingrectangle.hxx>
43 #include <svx/sdr/contact/objectcontact.hxx>
44 #include <svx/sdr/overlay/overlaymanager.hxx>
45 #include <svx/sdr/overlay/overlayselection.hxx>
46 #include <svx/sdr/contact/viewcontact.hxx>
47 #include <svx/sdr/contact/viewobjectcontact.hxx>
48 #include <svx/sdrpaintwindow.hxx>
49 #include <svx/sdrpagewindow.hxx>
50 #include <svx/sdrhittesthelper.hxx>
51 #include <vcl/uitest/logger.hxx>
52 #include <vcl/uitest/eventdescription.hxx>
53 #include <vcl/window.hxx>
54 #include <o3tl/string_view.hxx>
56 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
57 #include <comphelper/lok.hxx>
58 #include <sfx2/lokhelper.hxx>
59 #include <sfx2/lokcomponenthelpers.hxx>
60 #include <sfx2/viewsh.hxx>
61 #include <svtools/optionsdrawinglayer.hxx>
63 #include <array>
65 #include <com/sun/star/frame/XController.hpp>
66 #include <com/sun/star/view/XSelectionSupplier.hpp>
68 #include <boost/property_tree/json_parser.hpp>
70 using namespace com::sun::star;
72 // Migrate Marking of Objects, Points and GluePoints
74 class ImplMarkingOverlay
76 // The OverlayObjects
77 sdr::overlay::OverlayObjectList maObjects;
79 // The remembered second position in logical coordinates
80 basegfx::B2DPoint maSecondPosition;
82 // A flag to remember if the action is for unmarking.
83 bool mbUnmarking : 1;
85 public:
86 ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking);
88 // The OverlayObjects are cleared using the destructor of OverlayObjectList.
89 // That destructor calls clear() at the list which removes all objects from the
90 // OverlayManager and deletes them.
92 void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
93 bool IsUnmarking() const { return mbUnmarking; }
96 ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
97 : maSecondPosition(rStartPos),
98 mbUnmarking(bUnmarking)
100 if (comphelper::LibreOfficeKit::isActive())
101 return; // We do client-side object manipulation with the Kit API
103 for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
105 SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
106 const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
108 if (xTargetOverlay.is())
110 std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(new sdr::overlay::OverlayRollingRectangleStriped(
111 rStartPos, rStartPos, false));
112 xTargetOverlay->add(*pNew);
113 maObjects.append(std::move(pNew));
118 void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
120 if(rNewPosition != maSecondPosition)
122 // apply to OverlayObjects
123 for(sal_uInt32 a(0); a < maObjects.count(); a++)
125 sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
126 rCandidate.setSecondPosition(rNewPosition);
129 // remember new position
130 maSecondPosition = rNewPosition;
134 class MarkingSubSelectionOverlay
136 sdr::overlay::OverlayObjectList maObjects;
138 public:
139 MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
141 if (comphelper::LibreOfficeKit::isActive())
142 return; // We do client-side object manipulation with the Kit API
144 for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
146 SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
147 const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
149 if (xTargetOverlay.is())
151 const Color aHighlightColor = SvtOptionsDrawinglayer::getHilightColor();
153 std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
154 std::make_unique<sdr::overlay::OverlaySelection>(
155 sdr::overlay::OverlayType::Transparent,
156 aHighlightColor, std::vector(rSelections), false);
158 xTargetOverlay->add(*pNew);
159 maObjects.append(std::move(pNew));
165 SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
166 : SdrSnapView(rSdrModel, pOut)
167 , mpMarkedObj(nullptr)
168 , mpMarkedPV(nullptr)
169 , maHdlList(this)
170 , meDragMode(SdrDragMode::Move)
171 , meEditMode(SdrViewEditMode::Edit)
172 , meEditMode0(SdrViewEditMode::Edit)
173 , mbDesignMode(false)
174 , mbForceFrameHandles(false)
175 , mbPlusHdlAlways(false)
176 , mbInsPolyPoint(false)
177 , mbMarkedObjRectDirty(false)
178 , mbMrkPntDirty(false)
179 , mbMarkedPointsRectsDirty(false)
180 , mbMarkHandlesHidden(false)
181 , mbNegativeX(false)
184 BrkMarkObj();
185 BrkMarkPoints();
186 BrkMarkGluePoints();
188 StartListening(rSdrModel);
191 SdrMarkView::~SdrMarkView()
193 // Migrate selections
194 BrkMarkObj();
195 BrkMarkPoints();
196 BrkMarkGluePoints();
199 void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
201 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
203 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
204 SdrHintKind eKind=pSdrHint->GetKind();
205 if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
207 mbMarkedObjRectDirty=true;
208 mbMarkedPointsRectsDirty=true;
211 SdrSnapView::Notify(rBC,rHint);
214 void SdrMarkView::ModelHasChanged()
216 SdrPaintView::ModelHasChanged();
217 GetMarkedObjectListWriteAccess().SetNameDirty();
218 mbMarkedObjRectDirty=true;
219 mbMarkedPointsRectsDirty=true;
220 // Example: Obj is selected and maMarkedObjectList is sorted.
221 // In another View 2, the ObjOrder is changed (e. g. MovToTop())
222 // Then we need to re-sort MarkList.
223 GetMarkedObjectListWriteAccess().SetUnsorted();
224 SortMarkedObjects();
225 mbMrkPntDirty=true;
226 UndirtyMrkPnt();
227 SdrView* pV=static_cast<SdrView*>(this);
228 if (pV!=nullptr && !pV->IsDragObj() && !pV->IsInsObjPoint()) {
229 AdjustMarkHdl();
232 if (comphelper::LibreOfficeKit::isActive())
233 modelHasChangedLOKit();
236 void SdrMarkView::modelHasChangedLOKit()
238 if (GetMarkedObjectCount() <= 0)
239 return;
241 //TODO: Is MarkedObjRect valid at this point?
242 tools::Rectangle aSelection(GetMarkedObjRect());
243 tools::Rectangle* pResultSelection;
244 if (aSelection.IsEmpty())
245 pResultSelection = nullptr;
246 else
248 sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
249 if (nTotalPaintWindows == 1)
251 const OutputDevice* pOut = this->GetFirstOutputDevice();
252 const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
253 if (pWin && pWin->IsChart())
255 const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
256 if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
258 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
259 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
260 aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
265 // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
266 if (mpMarkedPV)
268 if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
270 if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
271 aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
275 pResultSelection = &aSelection;
277 if (mbNegativeX)
279 // Convert to positive X doc-coordinates
280 tools::Long nTmp = aSelection.Left();
281 aSelection.SetLeft(-aSelection.Right());
282 aSelection.SetRight(-nTmp);
286 if (SfxViewShell* pViewShell = GetSfxViewShell())
287 SfxLokHelper::notifyInvalidation(pViewShell, pResultSelection);
290 bool SdrMarkView::IsAction() const
292 return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
295 void SdrMarkView::MovAction(const Point& rPnt)
297 SdrSnapView::MovAction(rPnt);
299 if(IsMarkObj())
301 MovMarkObj(rPnt);
303 else if(IsMarkPoints())
305 MovMarkPoints(rPnt);
307 else if(IsMarkGluePoints())
309 MovMarkGluePoints(rPnt);
313 void SdrMarkView::EndAction()
315 if(IsMarkObj())
317 EndMarkObj();
319 else if(IsMarkPoints())
321 EndMarkPoints();
323 else if(IsMarkGluePoints())
325 EndMarkGluePoints();
328 SdrSnapView::EndAction();
331 void SdrMarkView::BckAction()
333 SdrSnapView::BckAction();
334 BrkMarkObj();
335 BrkMarkPoints();
336 BrkMarkGluePoints();
339 void SdrMarkView::BrkAction()
341 SdrSnapView::BrkAction();
342 BrkMarkObj();
343 BrkMarkPoints();
344 BrkMarkGluePoints();
347 void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
349 if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
351 rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
353 else
355 SdrSnapView::TakeActionRect(rRect);
360 void SdrMarkView::ClearPageView()
362 UnmarkAllObj();
363 SdrSnapView::ClearPageView();
366 void SdrMarkView::HideSdrPage()
368 bool bMrkChg(false);
370 SdrPageView* pPageView = GetSdrPageView();
371 if (pPageView)
373 // break all creation actions when hiding page (#75081#)
374 BrkAction();
376 // Discard all selections on this page
377 bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
380 SdrSnapView::HideSdrPage();
382 if(bMrkChg)
384 MarkListHasChanged();
385 AdjustMarkHdl();
390 void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
392 BrkAction();
394 DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");
396 basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
397 mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
399 maDragStat.Reset(rPnt);
400 maDragStat.NextPoint();
401 maDragStat.SetMinMove(mnMinMovLog);
404 void SdrMarkView::MovMarkObj(const Point& rPnt)
406 if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
408 maDragStat.NextMove(rPnt);
409 DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
410 basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
411 mpMarkObjOverlay->SetSecondPosition(aNewPos);
415 bool SdrMarkView::EndMarkObj()
417 bool bRetval(false);
419 if(IsMarkObj())
421 if(maDragStat.IsMinMoved())
423 tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
424 aRect.Normalize();
425 MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
426 bRetval = true;
429 // cleanup
430 BrkMarkObj();
433 return bRetval;
436 void SdrMarkView::BrkMarkObj()
438 if(IsMarkObj())
440 DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
441 mpMarkObjOverlay.reset();
446 bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
448 if(HasMarkablePoints())
450 BrkAction();
452 DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
453 basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
454 mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
456 maDragStat.Reset(rPnt);
457 maDragStat.NextPoint();
458 maDragStat.SetMinMove(mnMinMovLog);
460 return true;
463 return false;
466 void SdrMarkView::MovMarkPoints(const Point& rPnt)
468 if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
470 maDragStat.NextMove(rPnt);
472 DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
473 basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
474 mpMarkPointsOverlay->SetSecondPosition(aNewPos);
478 bool SdrMarkView::EndMarkPoints()
480 bool bRetval(false);
482 if(IsMarkPoints())
484 if(maDragStat.IsMinMoved())
486 tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
487 aRect.Normalize();
488 MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());
490 bRetval = true;
493 // cleanup
494 BrkMarkPoints();
497 return bRetval;
500 void SdrMarkView::BrkMarkPoints()
502 if(IsMarkPoints())
504 DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
505 mpMarkPointsOverlay.reset();
510 bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
512 if(HasMarkableGluePoints())
514 BrkAction();
516 DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");
518 basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
519 mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
520 maDragStat.Reset(rPnt);
521 maDragStat.NextPoint();
522 maDragStat.SetMinMove(mnMinMovLog);
524 return true;
527 return false;
530 void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
532 if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
534 maDragStat.NextMove(rPnt);
536 DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
537 basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
538 mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
542 void SdrMarkView::EndMarkGluePoints()
544 if(IsMarkGluePoints())
546 if(maDragStat.IsMinMoved())
548 tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
549 aRect.Normalize();
550 MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
553 // cleanup
554 BrkMarkGluePoints();
558 void SdrMarkView::BrkMarkGluePoints()
560 if(IsMarkGluePoints())
562 DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
563 mpMarkGluePointsOverlay.reset();
567 bool SdrMarkView::MarkableObjectsExceed( int n ) const
569 SdrPageView* pPV = GetSdrPageView();
570 if (!pPV)
571 return false;
573 SdrObjList* pOL=pPV->GetObjList();
574 const size_t nObjCount = pOL->GetObjCount();
575 for (size_t nObjNum=0; nObjNum<nObjCount; ++nObjNum) {
576 SdrObject* pObj=pOL->GetObj(nObjNum);
577 if (IsObjMarkable(pObj,pPV) && --n<0)
578 return true;
581 return false;
584 void SdrMarkView::hideMarkHandles()
586 if(!mbMarkHandlesHidden)
588 mbMarkHandlesHidden = true;
589 AdjustMarkHdl();
593 void SdrMarkView::showMarkHandles()
595 if(mbMarkHandlesHidden)
597 mbMarkHandlesHidden = false;
598 AdjustMarkHdl();
602 bool SdrMarkView::ImpIsFrameHandles() const
604 const size_t nMarkCount=GetMarkedObjectCount();
605 bool bFrmHdl=nMarkCount>static_cast<size_t>(mnFrameHandlesLimit) || mbForceFrameHandles;
606 bool bStdDrag=meDragMode==SdrDragMode::Move;
607 if (nMarkCount==1 && bStdDrag && bFrmHdl)
609 const SdrObject* pObj=GetMarkedObjectByIndex(0);
610 if (pObj && pObj->GetObjInventor()==SdrInventor::Default)
612 SdrObjKind nIdent=pObj->GetObjIdentifier();
613 if (nIdent==SdrObjKind::Line || nIdent==SdrObjKind::Edge || nIdent==SdrObjKind::Caption || nIdent==SdrObjKind::Measure || nIdent==SdrObjKind::CustomShape || nIdent==SdrObjKind::Table )
615 bFrmHdl=false;
619 if (!bStdDrag && !bFrmHdl) {
620 // all other drag modes only with FrameHandles
621 bFrmHdl=true;
622 if (meDragMode==SdrDragMode::Rotate) {
623 // when rotating, use ObjOwn drag, if there's at least 1 PolyObj
624 for (size_t nMarkNum=0; nMarkNum<nMarkCount && bFrmHdl; ++nMarkNum) {
625 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
626 const SdrObject* pObj=pM->GetMarkedSdrObj();
627 bFrmHdl=!pObj->IsPolyObj();
631 if (!bFrmHdl) {
632 // FrameHandles, if at least 1 Obj can't do SpecialDrag
633 for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bFrmHdl; ++nMarkNum) {
634 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
635 const SdrObject* pObj=pM->GetMarkedSdrObj();
636 bFrmHdl=!pObj->hasSpecialDrag();
640 // no FrameHdl for crop
641 if(bFrmHdl && SdrDragMode::Crop == meDragMode)
643 bFrmHdl = false;
646 return bFrmHdl;
649 namespace
651 std::u16string_view lcl_getDragMethodServiceName( std::u16string_view rCID )
653 std::u16string_view aRet;
655 size_t nIndexStart = rCID.find( u"DragMethod=" );
656 if( nIndexStart != std::u16string_view::npos )
658 nIndexStart = rCID.find( '=', nIndexStart );
659 if( nIndexStart != std::u16string_view::npos )
661 nIndexStart++;
662 size_t nNextSlash = rCID.find( '/', nIndexStart );
663 if( nNextSlash != std::u16string_view::npos )
665 sal_Int32 nIndexEnd = nNextSlash;
666 size_t nNextColon = rCID.find( ':', nIndexStart );
667 if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
668 nIndexEnd = nNextColon;
669 aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
673 return aRet;
676 std::u16string_view lcl_getDragParameterString( std::u16string_view rCID )
678 std::u16string_view aRet;
680 size_t nIndexStart = rCID.find( u"DragParameter=" );
681 if( nIndexStart != std::u16string_view::npos )
683 nIndexStart = rCID.find( '=', nIndexStart );
684 if( nIndexStart != std::u16string_view::npos )
686 nIndexStart++;
687 size_t nNextSlash = rCID.find( '/', nIndexStart );
688 if( nNextSlash != std::u16string_view::npos )
690 sal_Int32 nIndexEnd = nNextSlash;
691 size_t nNextColon = rCID.find( ':', nIndexStart );
692 if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
693 nIndexEnd = nNextColon;
694 aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
698 return aRet;
700 } // anonymous namespace
702 bool SdrMarkView::dumpGluePointsToJSON(boost::property_tree::ptree& rTree)
704 bool result = false;
705 tools::Long nSignX = mbNegativeX ? -1 : 1;
706 if (OutputDevice* pOutDev = mpMarkedPV ? mpMarkedPV->GetView().GetFirstOutputDevice() : nullptr)
708 bool bConvertUnit = false;
709 if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
710 bConvertUnit = true;
711 const SdrObjList* pOL = mpMarkedPV->GetObjList();
712 if (!pOL)
713 return false;
714 const size_t nObjCount = pOL->GetObjCount();
715 boost::property_tree::ptree elements;
716 for (size_t nObjNum = 0; nObjNum < nObjCount; ++nObjNum)
718 SdrObject* pObj = pOL->GetObj(nObjNum);
719 if (!pObj)
720 continue;
721 if (pObj == GetMarkedObjectByIndex(0))
722 continue;
723 const SdrGluePointList* pGPL = pObj->GetGluePointList();
724 bool VertexObject = !(pGPL && pGPL->GetCount());
725 const size_t count = !VertexObject ? pGPL->GetCount() : 4;
726 boost::property_tree::ptree object;
727 boost::property_tree::ptree points;
728 for (size_t i = 0; i < count; ++i)
730 boost::property_tree::ptree node;
731 boost::property_tree::ptree point;
732 const SdrGluePoint& rGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
733 Point rPoint = rGP.GetAbsolutePos(*pObj);
734 if (bConvertUnit)
736 rPoint = o3tl::convert(rPoint, o3tl::Length::mm100, o3tl::Length::twip);
738 point.put("x", nSignX * rPoint.getX());
739 point.put("y", rPoint.getY());
740 node.add_child("point", point);
741 points.push_back(std::make_pair("", node));
743 basegfx::B2DVector aGridOffset(0.0, 0.0);
744 Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
745 if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
747 Point p(aGridOffset.getX(), aGridOffset.getY());
748 if (bConvertUnit)
750 p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
752 boost::property_tree::ptree gridOffset;
753 gridOffset.put("x", nSignX * p.getX());
754 gridOffset.put("y", p.getY());
755 object.add_child("gridoffset", gridOffset);
757 object.put("ordnum", pObj->GetOrdNum());
758 object.add_child("gluepoints", points);
759 elements.push_back(std::make_pair("", object));
760 result = true;
762 rTree.add_child("shapes", elements);
764 return result;
767 void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
769 SfxViewShell* pViewShell = GetSfxViewShell();
771 tools::Rectangle aSelection(rRect);
772 tools::Long nSignX = mbNegativeX ? -1 : 1;
773 bool bIsChart = false;
774 Point addLogicOffset(0, 0);
775 bool convertMapMode = false;
776 if (!rRect.IsEmpty())
778 sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
779 if (nTotalPaintWindows == 1)
781 const OutputDevice* pOut = this->GetFirstOutputDevice();
782 const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
783 if (pWin && pWin->IsChart())
785 bIsChart = true;
786 const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
787 if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
789 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
790 if (mbNegativeX && AllSettings::GetLayoutRTL())
792 // mbNegativeX is set only for Calc in RTL mode.
793 // If global RTL flag is set, vcl-window X offset of chart window is
794 // mirrored w.r.t parent window rectangle. This needs to be reverted.
795 aOffsetPx.setX(pViewShellWindow->GetOutOffXPixel() + pViewShellWindow->GetSizePixel().Width()
796 - pWin->GetOutOffXPixel() - pWin->GetSizePixel().Width());
798 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
799 addLogicOffset = aLogicOffset;
800 aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
806 if (!aSelection.IsEmpty())
808 // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
809 if (mpMarkedPV)
811 if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
813 if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
815 aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
816 convertMapMode = true;
821 // hide the text selection too
822 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "");
826 OString sSelectionText;
827 OString sSelectionTextView;
828 boost::property_tree::ptree aTableJsonTree;
829 boost::property_tree::ptree aGluePointsTree;
830 bool bTableSelection = false;
831 bool bConnectorSelection = false;
833 if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Table)
835 auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
836 bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
838 if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Edge)
840 bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
842 if (GetMarkedObjectCount())
844 SdrMark* pM = GetSdrMarkByIndex(0);
845 SdrObject* pO = pM->GetMarkedSdrObj();
846 Degree100 nRotAngle = pO->GetRotateAngle();
847 // true if we are dealing with a RotGrfFlyFrame
848 // (SwVirtFlyDrawObj with a SwGrfNode)
849 bool bWriterGraphic = pO->HasLimitedRotation();
851 OStringBuffer aExtraInfo;
852 OString handleArrayStr;
854 aExtraInfo.append("{\"id\":\""
855 + OString::number(reinterpret_cast<sal_IntPtr>(pO))
856 + "\",\"type\":"
857 + OString::number(static_cast<sal_Int32>(pO->GetObjIdentifier())));
859 // In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
860 // In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
861 // SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
862 // but the rotation is not applied to the LogicRect. Therefore,
863 // what we calculate in online does not match with the core in case of the rotation.
864 // Here we can send the correct gridOffset in the selection callback, this way
865 // whether the shape is rotated or not, we will always have the correct gridOffset
866 // Note that the gridOffset is calculated from the first selected obj
867 basegfx::B2DVector aGridOffset(0.0, 0.0);
868 if(getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
870 Point p(aGridOffset.getX(), aGridOffset.getY());
871 if (convertMapMode)
872 p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
873 aExtraInfo.append(",\"gridOffsetX\":"
874 + OString::number(nSignX * p.getX())
875 + ",\"gridOffsetY\":"
876 + OString::number(p.getY()));
879 if (bWriterGraphic)
881 aExtraInfo.append(", \"isWriterGraphic\": true");
883 else if (bIsChart)
885 LokChartHelper aChartHelper(pViewShell);
886 css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
887 css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
888 if (xSelectionSupplier.is())
890 uno::Any aSel = xSelectionSupplier->getSelection();
891 OUString aValue;
892 if (aSel >>= aValue)
894 OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
895 const std::vector<OString> aProps{"Draggable", "Resizable", "Rotatable"};
896 for (const auto& rProp: aProps)
898 sal_Int32 nPos = aObjectCID.indexOf(rProp);
899 if (nPos == -1) continue;
900 nPos += rProp.getLength() + 1; // '='
901 if (aExtraInfo.getLength() > 2) // != "{ "
902 aExtraInfo.append(", ");
903 aExtraInfo.append("\"is" + rProp + "\": "
904 + OString::boolean(aObjectCID[nPos] == '1'));
907 std::u16string_view sDragMethod = lcl_getDragMethodServiceName(aValue);
908 if (sDragMethod == u"PieSegmentDragging")
910 // old initial offset inside the CID returned by xSelectionSupplier->getSelection()
911 // after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
912 aValue = pO->GetName();
913 std::u16string_view sDragParameters = lcl_getDragParameterString(aValue);
914 if (!sDragParameters.empty())
916 aExtraInfo.append(", \"dragInfo\": { "
917 "\"dragMethod\": \""
918 + OUString(sDragMethod).toUtf8()
919 + "\"");
921 sal_Int32 nStartIndex = 0;
922 std::array<int, 5> aDragParameters;
923 for (auto& rParam : aDragParameters)
925 std::u16string_view sParam = o3tl::getToken(sDragParameters, 0, ',', nStartIndex);
926 if (sParam.empty())
927 break;
928 rParam = o3tl::toInt32(sParam);
931 // initial offset in %
932 if (aDragParameters[0] < 0)
933 aDragParameters[0] = 0;
934 else if (aDragParameters[0] > 100)
935 aDragParameters[0] = 100;
937 aExtraInfo.append(", \"initialOffset\": "
938 + OString::number(static_cast<sal_Int32>(aDragParameters[0])));
940 // drag direction constraint
941 Point aMinPos(aDragParameters[1], aDragParameters[2]);
942 Point aMaxPos(aDragParameters[3], aDragParameters[4]);
943 Point aDragDirection = aMaxPos - aMinPos;
944 aDragDirection = o3tl::convert(aDragDirection, o3tl::Length::mm100, o3tl::Length::twip);
946 aExtraInfo.append(", \"dragDirection\": ["
947 + aDragDirection.toString()
948 + "]");
950 // polygon approximating the pie segment or donut segment
951 if (pO->GetObjIdentifier() == SdrObjKind::PathFill)
953 const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
954 if (aPolyPolygon.count() == 1)
956 const basegfx::B2DPolygon aPolygon = aPolyPolygon.getB2DPolygon(0);
957 if (sal_uInt32 nPolySize = aPolygon.count())
959 const OutputDevice* pOut = this->GetFirstOutputDevice();
960 const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
961 const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
962 if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
964 // in the following code escaping sequences used inside raw literal strings
965 // are for making them understandable by the JSON parser
967 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
968 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
969 OString sPolygonElem("<polygon points=\\\"");
970 for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
972 const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
973 Point aPoint(aB2Point.getX(), aB2Point.getY());
974 aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
975 if (mbNegativeX)
976 aPoint.setX(-aPoint.X());
977 if (nIndex > 0)
978 sPolygonElem += " ";
979 sPolygonElem += aPoint.toString();
981 sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";
983 OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
984 OString::number(aSelection.GetWidth() / 100.0) +
985 R"elem(mm\" height=\")elem" +
986 OString::number(aSelection.GetHeight() / 100.0) +
987 R"elem(mm\" viewBox=\")elem" +
988 aSelection.toString() +
989 R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";
991 aExtraInfo.append(", \"svg\": \""
992 + sSVGElem
993 + "\\n "
994 + sPolygonElem
995 + "\\n</svg>"
996 "\""); // svg
1001 aExtraInfo.append("}"); // dragInfo
1007 if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
1009 boost::property_tree::ptree responseJSON;
1010 boost::property_tree::ptree others;
1011 boost::property_tree::ptree anchor;
1012 boost::property_tree::ptree rectangle;
1013 boost::property_tree::ptree poly;
1014 boost::property_tree::ptree custom;
1015 boost::property_tree::ptree nodes;
1016 for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
1018 SdrHdl *pHdl = maHdlList.GetHdl(i);
1019 boost::property_tree::ptree child;
1020 boost::property_tree::ptree point;
1021 sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
1022 child.put("id", pHdl->GetObjHdlNum());
1023 child.put("kind", kind);
1024 child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
1025 Point pHdlPos = pHdl->GetPos();
1026 pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
1027 if (convertMapMode)
1029 pHdlPos = o3tl::convert(pHdlPos, o3tl::Length::mm100, o3tl::Length::twip);
1031 point.put("x", pHdlPos.getX());
1032 point.put("y", pHdlPos.getY());
1033 child.add_child("point", point);
1034 const auto node = std::make_pair("", child);
1035 boost::property_tree::ptree* selectedNode = nullptr;
1036 if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
1038 selectedNode = &rectangle;
1040 else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
1042 selectedNode = &poly;
1044 else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
1046 selectedNode = &custom;
1048 else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
1050 if (getSdrModelFromSdrView().IsWriter())
1051 selectedNode = &anchor;
1052 else
1053 // put it to others as we don't render them except in writer
1054 selectedNode = &others;
1056 else
1058 selectedNode = &others;
1060 std::string sKind = std::to_string(kind);
1061 boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
1062 if (!kindNode)
1064 boost::property_tree::ptree newChild;
1065 newChild.push_back(node);
1066 selectedNode->add_child(sKind.c_str(), newChild);
1068 else
1069 kindNode.get().push_back(node);
1071 nodes.add_child("rectangle", rectangle);
1072 nodes.add_child("poly", poly);
1073 nodes.add_child("custom", custom);
1074 nodes.add_child("anchor", anchor);
1075 nodes.add_child("others", others);
1076 responseJSON.add_child("kinds", nodes);
1077 std::stringstream aStream;
1078 boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
1079 handleArrayStr = ", \"handles\":";
1080 handleArrayStr = handleArrayStr + aStream.str().c_str();
1081 if (bConnectorSelection)
1083 aStream.str("");
1084 boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
1085 handleArrayStr = handleArrayStr + ", \"GluePoints\":";
1086 handleArrayStr = handleArrayStr + aStream.str().c_str();
1090 if (mbNegativeX)
1092 tools::Rectangle aNegatedRect(aSelection);
1093 aNegatedRect.SetLeft(-aNegatedRect.Left());
1094 aNegatedRect.SetRight(-aNegatedRect.Right());
1095 aNegatedRect.Normalize();
1096 sSelectionText = aNegatedRect.toString() +
1097 ", " + OString::number(nRotAngle.get());
1099 else
1101 sSelectionText = aSelection.toString() +
1102 ", " + OString::number(nRotAngle.get());
1104 if (!aExtraInfo.isEmpty())
1106 sSelectionTextView = sSelectionText + ", " + aExtraInfo + "}";
1107 aExtraInfo.append(handleArrayStr
1108 + "}");
1109 sSelectionText += ", " + aExtraInfo;
1110 aExtraInfo.setLength(0);
1114 if (sSelectionText.isEmpty())
1116 sSelectionText = "EMPTY";
1117 sSelectionTextView = "EMPTY";
1118 if (!pOtherShell)
1119 pViewShell->NotifyOtherViews(LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", OString());
1122 if (bTableSelection)
1124 boost::property_tree::ptree aTableRectangle;
1125 aTableRectangle.put("x", aSelection.Left());
1126 aTableRectangle.put("y", aSelection.Top());
1127 aTableRectangle.put("width", aSelection.GetWidth());
1128 aTableRectangle.put("height", aSelection.GetHeight());
1129 aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));
1131 std::stringstream aStream;
1132 boost::property_tree::write_json(aStream, aTableJsonTree);
1133 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, OString(aStream.str()));
1135 else if (!getSdrModelFromSdrView().IsWriter())
1137 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}");
1140 if (pOtherShell)
1142 // Another shell wants to know about our existing
1143 // selection.
1144 if (pViewShell != pOtherShell)
1145 SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
1147 else
1149 // We have a new selection, so both pViewShell and the
1150 // other views want to know about it.
1151 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText);
1152 SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
1155 if (comphelper::LibreOfficeKit::isActive() && mpMarkedObj
1156 && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Media)
1158 SdrMediaObj* mediaObj = dynamic_cast<SdrMediaObj*>(mpMarkedObj);
1159 if (mediaObj)
1160 mediaObj->notifyPropertiesForLOKit();
1165 void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
1167 // remember old focus handle values to search for it again
1168 const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
1169 bool bSaveOldFocus(false);
1170 sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
1171 SdrHdlKind eSaveKind(SdrHdlKind::Move);
1172 SdrObject* pSaveObj = nullptr;
1174 mpMarkingSubSelectionOverlay.reset();
1176 if(pSaveOldFocusHdl
1177 && pSaveOldFocusHdl->GetObj()
1178 && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
1179 && (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
1181 bSaveOldFocus = true;
1182 nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
1183 nSavePointNum = pSaveOldFocusHdl->GetPointNum();
1184 pSaveObj = pSaveOldFocusHdl->GetObj();
1185 eSaveKind = pSaveOldFocusHdl->GetKind();
1188 // delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
1189 maHdlList.Clear();
1190 maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
1191 maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
1192 mpMarkedObj=nullptr;
1193 mpMarkedPV=nullptr;
1195 // are handles enabled at all? Create only then
1196 if(areMarkHandlesHidden())
1197 return;
1199 // There can be multiple mark views, but we're only interested in the one that has a window associated.
1200 const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;
1202 const size_t nMarkCount=GetMarkedObjectCount();
1203 bool bStdDrag=meDragMode==SdrDragMode::Move;
1204 bool bSingleTextObjMark=false;
1205 bool bLimitedRotation(false);
1207 if (nMarkCount==1)
1209 mpMarkedObj=GetMarkedObjectByIndex(0);
1211 if(nullptr != mpMarkedObj)
1213 bSingleTextObjMark =
1214 DynCastSdrTextObj( mpMarkedObj) != nullptr &&
1215 static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
1217 // RotGrfFlyFrame: we may have limited rotation
1218 bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
1222 bool bFrmHdl=ImpIsFrameHandles();
1224 if (nMarkCount>0)
1226 mpMarkedPV=GetSdrPageViewOfMarkedByIndex(0);
1228 for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
1230 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1232 if (mpMarkedPV!=pM->GetPageView())
1234 mpMarkedPV=nullptr;
1239 SfxViewShell* pViewShell = GetSfxViewShell();
1241 // check if text edit or ole is active and handles need to be suppressed. This may be the case
1242 // when a single object is selected
1243 // Using a strict return statement is okay here; no handles means *no* handles.
1244 if(mpMarkedObj)
1246 // formerly #i33755#: If TextEdit is active the EditEngine will directly paint
1247 // to the window, so suppress Overlay and handles completely; a text frame for
1248 // the active text edit will be painted by the repaint mechanism in
1249 // SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
1250 // in the future
1251 // Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
1252 if(static_cast<SdrView*>(this)->IsTextEdit())
1254 const SdrTextObj* pSdrTextObj = DynCastSdrTextObj(mpMarkedObj);
1256 if (pSdrTextObj && pSdrTextObj->IsInEditMode())
1258 if (!bTiledRendering)
1259 return;
1263 // formerly #i118524#: if inplace activated OLE is selected, suppress handles
1264 const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);
1266 if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
1268 return;
1271 if (!maSubSelectionList.empty())
1273 mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
1277 tools::Rectangle aRect(GetMarkedObjRect());
1279 if (bFrmHdl)
1281 if(!aRect.IsEmpty())
1283 // otherwise nothing is found
1284 const size_t nSiz0(maHdlList.GetHdlCount());
1286 if( bSingleTextObjMark )
1288 mpMarkedObj->AddToHdlList(maHdlList);
1290 else
1292 const bool bWdt0(aRect.Left() == aRect.Right());
1293 const bool bHgt0(aRect.Top() == aRect.Bottom());
1295 if (bWdt0 && bHgt0)
1297 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1299 else if (!bStdDrag && (bWdt0 || bHgt0))
1301 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1302 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
1304 else
1306 if (!bWdt0 && !bHgt0)
1308 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1311 if (!bLimitedRotation && !bHgt0)
1313 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
1316 if (!bWdt0 && !bHgt0)
1318 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
1321 if (!bLimitedRotation && !bWdt0)
1323 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
1324 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
1327 if (!bWdt0 && !bHgt0)
1329 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
1332 if (!bLimitedRotation && !bHgt0)
1334 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
1337 if (!bWdt0 && !bHgt0)
1339 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
1344 // Diagram selection visualization support
1345 // Caution: CppunitTest_sd_tiledrendering shows that mpMarkedObj *can* actually be nullptr (!)
1346 if(nullptr != mpMarkedObj && mpMarkedObj->isDiagram())
1348 mpMarkedObj->AddToHdlList(maHdlList);
1351 const size_t nSiz1(maHdlList.GetHdlCount());
1353 // moved setting the missing parameters at SdrHdl here from the
1354 // single loop above (bSingleTextObjMark), this was missing all
1355 // the time. Setting SdrObject is now required to correctly get
1356 // the View-Dependent evtl. GridOffset adapted
1357 for (size_t i=nSiz0; i<nSiz1; ++i)
1359 SdrHdl* pHdl=maHdlList.GetHdl(i);
1360 pHdl->SetObj(mpMarkedObj);
1361 pHdl->SetPageView(mpMarkedPV);
1362 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1366 else
1368 bool bDone(false);
1370 // moved crop handling to non-frame part and the handle creation to SdrGrafObj
1371 if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
1373 // Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
1374 // behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
1375 // writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
1376 const size_t nSiz0(maHdlList.GetHdlCount());
1377 mpMarkedObj->addCropHandles(maHdlList);
1378 const size_t nSiz1(maHdlList.GetHdlCount());
1380 // Was missing: Set infos at SdrCropHdl
1381 for (size_t i=nSiz0; i<nSiz1; ++i)
1383 SdrHdl* pHdl=maHdlList.GetHdl(i);
1384 pHdl->SetObj(mpMarkedObj);
1385 pHdl->SetPageView(mpMarkedPV);
1386 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1389 bDone = true;
1392 if(!bDone)
1394 for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
1396 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1397 SdrObject* pObj=pM->GetMarkedSdrObj();
1398 SdrPageView* pPV=pM->GetPageView();
1399 const size_t nSiz0=maHdlList.GetHdlCount();
1400 pObj->AddToHdlList(maHdlList);
1401 const size_t nSiz1=maHdlList.GetHdlCount();
1402 bool bPoly=pObj->IsPolyObj();
1403 const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
1404 for (size_t i=nSiz0; i<nSiz1; ++i)
1406 SdrHdl* pHdl=maHdlList.GetHdl(i);
1407 pHdl->SetObj(pObj);
1408 pHdl->SetPageView(pPV);
1409 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1411 if (bPoly)
1413 bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
1414 pHdl->SetSelected(bSelected);
1415 if (mbPlusHdlAlways || bSelected)
1417 SdrHdlList plusList(nullptr);
1418 pObj->AddToPlusHdlList(plusList, *pHdl);
1419 sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
1420 for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
1422 SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
1423 pPlusHdl->SetObj(pObj);
1424 pPlusHdl->SetPageView(pPV);
1425 pPlusHdl->SetPlusHdl(true);
1427 plusList.MoveTo(maHdlList);
1435 // GluePoint handles
1436 for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
1438 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1439 SdrObject* pObj=pM->GetMarkedSdrObj();
1440 const SdrGluePointList* pGPL=pObj->GetGluePointList();
1441 if (!pGPL)
1442 continue;
1444 SdrPageView* pPV=pM->GetPageView();
1445 const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
1446 for (sal_uInt16 nId : rMrkGlue)
1448 //nNum changed to nNumGP because already used in for loop
1449 sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
1450 if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
1452 const SdrGluePoint& rGP=(*pGPL)[nNumGP];
1453 Point aPos(rGP.GetAbsolutePos(*pObj));
1454 std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
1455 pGlueHdl->SetObj(pObj);
1456 pGlueHdl->SetPageView(pPV);
1457 pGlueHdl->SetObjHdlNum(nId);
1458 maHdlList.AddHdl(std::move(pGlueHdl));
1463 // rotation point/axis of reflection
1464 if(!bLimitedRotation)
1466 AddDragModeHdl(meDragMode);
1469 // sort handles
1470 maHdlList.Sort();
1472 // add custom handles (used by other apps, e.g. AnchorPos)
1473 AddCustomHdl();
1475 // moved it here to access all the handles for callback.
1476 if (bTiledRendering && pViewShell)
1478 SetMarkHandlesForLOKit(aRect, pOtherShell);
1481 // try to restore focus handle index from remembered values
1482 if(!bSaveOldFocus)
1483 return;
1485 for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
1487 SdrHdl* pCandidate = maHdlList.GetHdl(a);
1489 if(pCandidate->GetObj()
1490 && pCandidate->GetObj() == pSaveObj
1491 && pCandidate->GetKind() == eSaveKind
1492 && pCandidate->GetPolyNum() == nSavePolyNum
1493 && pCandidate->GetPointNum() == nSavePointNum)
1495 maHdlList.SetFocusHdl(pCandidate);
1496 break;
1501 void SdrMarkView::AddCustomHdl()
1503 // add custom handles (used by other apps, e.g. AnchorPos)
1506 void SdrMarkView::SetDragMode(SdrDragMode eMode)
1508 SdrDragMode eMode0=meDragMode;
1509 meDragMode=eMode;
1510 if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
1511 if (meDragMode!=eMode0) {
1512 ForceRefToMarked();
1513 SetMarkHandles(nullptr);
1515 if (AreObjectsMarked()) MarkListHasChanged();
1520 void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
1522 switch(eMode)
1524 case SdrDragMode::Rotate:
1526 // add rotation center
1527 maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
1528 break;
1530 case SdrDragMode::Mirror:
1532 // add axis of reflection
1533 std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
1534 std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
1535 std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));
1537 pHdl1->SetObjHdlNum(1); // for sorting
1538 pHdl2->SetObjHdlNum(2); // for sorting
1539 pHdl3->SetObjHdlNum(3); // for sorting
1541 maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
1542 maHdlList.AddHdl(std::move(pHdl2));
1543 maHdlList.AddHdl(std::move(pHdl3));
1545 break;
1547 case SdrDragMode::Transparence:
1549 // add interactive transparency handle
1550 const size_t nMarkCount = GetMarkedObjectCount();
1551 if(nMarkCount == 1)
1553 SdrObject* pObj = GetMarkedObjectByIndex(0);
1554 SdrModel& rModel = GetModel();
1555 const SfxItemSet& rSet = pObj->GetMergedItemSet();
1557 if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
1559 // add this item, it's not yet there
1560 XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
1561 basegfx::BGradient aGrad = aNewItem.GetGradientValue();
1563 aNewItem.SetEnabled(true);
1564 aGrad.SetStartIntens(100);
1565 aGrad.SetEndIntens(100);
1566 aNewItem.SetGradientValue(aGrad);
1568 // add undo to allow user to take back this step
1569 if (rModel.IsUndoEnabled())
1571 rModel.BegUndo(SvxResId(SIP_XA_FILLTRANSPARENCE));
1572 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
1573 rModel.EndUndo();
1576 SfxItemSet aNewSet(rModel.GetItemPool());
1577 aNewSet.Put(aNewItem);
1578 pObj->SetMergedItemSetAndBroadcast(aNewSet);
1581 // set values and transform to vector set
1582 GradTransVector aGradTransVector;
1583 GradTransGradient aGradTransGradient;
1585 aGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
1586 GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
1588 // build handles
1589 const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
1590 const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
1591 std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
1592 std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
1593 std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, false));
1594 DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
1596 // link them
1597 pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
1598 pGradHdl->SetObj(pObj);
1599 pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1600 pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1602 // insert them
1603 maHdlList.AddHdl(std::move(pColHdl1));
1604 maHdlList.AddHdl(std::move(pColHdl2));
1605 maHdlList.AddHdl(std::move(pGradHdl));
1607 break;
1609 case SdrDragMode::Gradient:
1611 // add interactive gradient handle
1612 const size_t nMarkCount = GetMarkedObjectCount();
1613 if(nMarkCount == 1)
1615 SdrObject* pObj = GetMarkedObjectByIndex(0);
1616 const SfxItemSet& rSet = pObj->GetMergedItemSet();
1617 drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
1619 if(eFillStyle == drawing::FillStyle_GRADIENT)
1621 // set values and transform to vector set
1622 GradTransVector aGradTransVector;
1623 GradTransGradient aGradTransGradient;
1624 Size aHdlSize(15, 15);
1626 aGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
1627 GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
1629 // build handles
1630 const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
1631 const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
1632 std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, aHdlSize, false));
1633 std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, aHdlSize, false));
1634 std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, true));
1635 DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
1637 // link them
1638 pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
1639 pGradHdl->SetObj(pObj);
1640 pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1641 pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1643 // insert them
1644 maHdlList.AddHdl(std::move(pColHdl1));
1645 maHdlList.AddHdl(std::move(pColHdl2));
1646 maHdlList.AddHdl(std::move(pGradHdl));
1649 break;
1651 case SdrDragMode::Crop:
1653 // TODO
1654 break;
1656 default: break;
1660 /** handle mouse over effects for handles */
1661 bool SdrMarkView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
1663 if(maHdlList.GetHdlCount())
1665 SdrHdl* pMouseOverHdl = nullptr;
1666 if( !rMEvt.IsLeaveWindow() && pWin )
1668 Point aMDPos( pWin->PixelToLogic( rMEvt.GetPosPixel() ) );
1669 pMouseOverHdl = PickHandle(aMDPos);
1672 // notify last mouse over handle that he lost the mouse
1673 const size_t nHdlCount = maHdlList.GetHdlCount();
1675 for(size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
1677 SdrHdl* pCurrentHdl = GetHdl(nHdl);
1678 if( pCurrentHdl->mbMouseOver )
1680 if( pCurrentHdl != pMouseOverHdl )
1682 pCurrentHdl->mbMouseOver = false;
1683 pCurrentHdl->onMouseLeave();
1685 break;
1689 // notify current mouse over handle
1690 if( pMouseOverHdl )
1692 pMouseOverHdl->mbMouseOver = true;
1693 pMouseOverHdl->onMouseEnter(rMEvt);
1696 return SdrSnapView::MouseMove(rMEvt, pWin);
1699 bool SdrMarkView::RequestHelp(const HelpEvent& rHEvt)
1701 if (maHdlList.GetHdlCount())
1703 const size_t nHdlCount = maHdlList.GetHdlCount();
1705 for (size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
1707 SdrHdl* pCurrentHdl = GetHdl(nHdl);
1708 if (pCurrentHdl->mbMouseOver)
1710 pCurrentHdl->onHelpRequest();
1711 return true;
1715 return SdrSnapView::RequestHelp(rHEvt);
1718 void SdrMarkView::ForceRefToMarked()
1720 switch(meDragMode)
1722 case SdrDragMode::Rotate:
1724 tools::Rectangle aR(GetMarkedObjRect());
1725 maRef1 = aR.Center();
1727 break;
1730 case SdrDragMode::Mirror:
1732 // first calculate the length of the axis of reflection
1733 tools::Long nOutMin=0;
1734 tools::Long nOutMax=0;
1735 tools::Long nMinLen=0;
1736 tools::Long nObjDst=0;
1737 tools::Long nOutHgt=0;
1738 OutputDevice* pOut=GetFirstOutputDevice();
1739 if (pOut!=nullptr) {
1740 // minimum length: 50 pixels
1741 nMinLen=pOut->PixelToLogic(Size(0,50)).Height();
1742 // 20 pixels distance to the Obj for the reference point
1743 nObjDst=pOut->PixelToLogic(Size(0,20)).Height();
1744 // MinY/MaxY
1745 // margin = minimum length = 10 pixels
1746 tools::Long nDst=pOut->PixelToLogic(Size(0,10)).Height();
1747 nOutMin=-pOut->GetMapMode().GetOrigin().Y();
1748 nOutMax=pOut->GetOutputSize().Height()-1+nOutMin;
1749 nOutMin+=nDst;
1750 nOutMax-=nDst;
1751 // absolute minimum length, however, is 10 pixels
1752 if (nOutMax-nOutMin<nDst) {
1753 nOutMin+=nOutMax+1;
1754 nOutMin/=2;
1755 nOutMin-=(nDst+1)/2;
1756 nOutMax=nOutMin+nDst;
1758 nOutHgt=nOutMax-nOutMin;
1759 // otherwise minimum length = 1/4 OutHgt
1760 tools::Long nTemp=nOutHgt/4;
1761 if (nTemp>nMinLen) nMinLen=nTemp;
1764 tools::Rectangle aR(GetMarkedObjBoundRect());
1765 Point aCenter(aR.Center());
1766 tools::Long nMarkHgt=aR.GetHeight()-1;
1767 tools::Long nHgt=nMarkHgt+nObjDst*2; // 20 pixels overlapping above and below
1768 if (nHgt<nMinLen) nHgt=nMinLen; // minimum length 50 pixels or 1/4 OutHgt, respectively
1770 tools::Long nY1=aCenter.Y()-(nHgt+1)/2;
1771 tools::Long nY2=nY1+nHgt;
1773 if (pOut!=nullptr && nMinLen>nOutHgt) nMinLen=nOutHgt; // TODO: maybe shorten this a little
1775 if (pOut!=nullptr) { // now move completely into the visible area
1776 if (nY1<nOutMin) {
1777 nY1=nOutMin;
1778 if (nY2<nY1+nMinLen) nY2=nY1+nMinLen;
1780 if (nY2>nOutMax) {
1781 nY2=nOutMax;
1782 if (nY1>nY2-nMinLen) nY1=nY2-nMinLen;
1786 maRef1.setX(aCenter.X() );
1787 maRef1.setY(nY1 );
1788 maRef2.setX(aCenter.X() );
1789 maRef2.setY(nY2 );
1791 break;
1794 case SdrDragMode::Transparence:
1795 case SdrDragMode::Gradient:
1796 case SdrDragMode::Crop:
1798 tools::Rectangle aRect(GetMarkedObjBoundRect());
1799 maRef1 = aRect.TopLeft();
1800 maRef2 = aRect.BottomRight();
1801 break;
1803 default: break;
1807 void SdrMarkView::SetRef1(const Point& rPt)
1809 if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
1811 maRef1 = rPt;
1812 SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref1);
1813 if(pH)
1814 pH->SetPos(rPt);
1818 void SdrMarkView::SetRef2(const Point& rPt)
1820 if(meDragMode == SdrDragMode::Mirror)
1822 maRef2 = rPt;
1823 SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref2);
1824 if(pH)
1825 pH->SetPos(rPt);
1829 SfxViewShell* SdrMarkView::GetSfxViewShell() const
1831 return SfxViewShell::Current();
1834 void SdrMarkView::CheckMarked()
1836 for (size_t nm=GetMarkedObjectCount(); nm>0;) {
1837 --nm;
1838 SdrMark* pM = GetSdrMarkByIndex(nm);
1839 SdrObject* pObj = pM->GetMarkedSdrObj();
1840 SdrPageView* pPV = pM->GetPageView();
1841 bool bRaus = !pObj || !pPV->IsObjMarkable(pObj);
1842 if (bRaus)
1844 GetMarkedObjectListWriteAccess().DeleteMark(nm);
1846 else
1848 if (!IsGluePointEditMode()) { // selected gluepoints only in GlueEditMode
1849 SdrUShortCont& rPts = pM->GetMarkedGluePoints();
1850 rPts.clear();
1855 // at least reset the remembered BoundRect to prevent handle
1856 // generation if bForceFrameHandles is TRUE.
1857 mbMarkedObjRectDirty = true;
1860 void SdrMarkView::SetMarkRects()
1862 SdrPageView* pPV = GetSdrPageView();
1864 if(pPV)
1866 pPV->SetHasMarkedObj(GetMarkedObjectList().TakeSnapRect(pPV, pPV->MarkSnap()));
1867 GetMarkedObjectList().TakeBoundRect(pPV, pPV->MarkBound());
1871 void SdrMarkView::SetFrameHandles(bool bOn)
1873 if (bOn!=mbForceFrameHandles) {
1874 bool bOld=ImpIsFrameHandles();
1875 mbForceFrameHandles=bOn;
1876 bool bNew=ImpIsFrameHandles();
1877 if (bNew!=bOld) {
1878 AdjustMarkHdl();
1879 MarkListHasChanged();
1884 void SdrMarkView::SetEditMode(SdrViewEditMode eMode)
1886 if (eMode==meEditMode) return;
1888 bool bGlue0=meEditMode==SdrViewEditMode::GluePointEdit;
1889 bool bEdge0=static_cast<SdrCreateView*>(this)->IsEdgeTool();
1890 meEditMode0=meEditMode;
1891 meEditMode=eMode;
1892 bool bGlue1=meEditMode==SdrViewEditMode::GluePointEdit;
1893 bool bEdge1=static_cast<SdrCreateView*>(this)->IsEdgeTool();
1894 // avoid flickering when switching between GlueEdit and EdgeTool
1895 if (bGlue1 && !bGlue0) ImpSetGlueVisible2(bGlue1);
1896 if (bEdge1!=bEdge0) ImpSetGlueVisible3(bEdge1);
1897 if (!bGlue1 && bGlue0) ImpSetGlueVisible2(bGlue1);
1898 if (bGlue0 && !bGlue1) UnmarkAllGluePoints();
1902 bool SdrMarkView::IsObjMarkable(SdrObject const * pObj, SdrPageView const * pPV) const
1904 if (pObj)
1906 if (pObj->IsMarkProtect() ||
1907 (!mbDesignMode && pObj->IsUnoObj()))
1909 // object not selectable or
1910 // SdrUnoObj not in DesignMode
1911 return false;
1914 return pPV==nullptr || pPV->IsObjMarkable(pObj);
1917 bool SdrMarkView::IsMarkedObjHit(const Point& rPnt, short nTol) const
1919 bool bRet=false;
1920 nTol=ImpGetHitTolLogic(nTol,nullptr);
1921 for (size_t nm=0; nm<GetMarkedObjectCount() && !bRet; ++nm) {
1922 SdrMark* pM=GetSdrMarkByIndex(nm);
1923 bRet = nullptr != CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr);
1925 return bRet;
1928 SdrHdl* SdrMarkView::PickHandle(const Point& rPnt) const
1930 if (mbSomeObjChgdFlag) { // recalculate handles, if necessary
1931 FlushComeBackTimer();
1933 return maHdlList.IsHdlListHit(rPnt);
1936 bool SdrMarkView::MarkObj(const Point& rPnt, short nTol, bool bToggle, bool bDeep)
1938 SdrPageView* pPV;
1939 nTol=ImpGetHitTolLogic(nTol,nullptr);
1940 SdrSearchOptions nOptions=SdrSearchOptions::PICKMARKABLE;
1941 if (bDeep) nOptions=nOptions|SdrSearchOptions::DEEP;
1942 SdrObject* pObj = PickObj(rPnt, static_cast<sal_uInt16>(nTol), pPV, nOptions);
1943 if (pObj) {
1944 bool bUnmark=bToggle && IsObjMarked(pObj);
1945 MarkObj(pObj,pPV,bUnmark);
1947 return pObj != nullptr;
1950 bool SdrMarkView::MarkNextObj(bool bPrev)
1952 SdrPageView* pPageView = GetSdrPageView();
1954 if(!pPageView)
1956 return false;
1959 SortMarkedObjects();
1960 const size_t nMarkCount=GetMarkedObjectCount();
1961 size_t nChgMarkNum = SAL_MAX_SIZE; // number of the MarkEntry we want to replace
1962 size_t nSearchObjNum = bPrev ? 0 : SAL_MAX_SIZE;
1963 if (nMarkCount!=0) {
1964 nChgMarkNum=bPrev ? 0 : nMarkCount-1;
1965 SdrMark* pM=GetSdrMarkByIndex(nChgMarkNum);
1966 OSL_ASSERT(pM!=nullptr);
1967 if (pM->GetMarkedSdrObj() != nullptr)
1968 nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition();
1971 SdrObject* pMarkObj=nullptr;
1972 SdrObjList* pSearchObjList=pPageView->GetObjList();
1973 const size_t nObjCount = pSearchObjList->GetObjCount();
1974 if (nObjCount!=0) {
1975 if (nSearchObjNum>nObjCount) nSearchObjNum=nObjCount;
1976 while (pMarkObj==nullptr && ((!bPrev && nSearchObjNum>0) || (bPrev && nSearchObjNum<nObjCount)))
1978 if (!bPrev)
1979 nSearchObjNum--;
1980 SdrObject* pSearchObj = pSearchObjList->GetObjectForNavigationPosition(nSearchObjNum);
1981 if (IsObjMarkable(pSearchObj,pPageView))
1983 if (TryToFindMarkedObject(pSearchObj)==SAL_MAX_SIZE)
1985 pMarkObj=pSearchObj;
1988 if (bPrev) nSearchObjNum++;
1992 if(!pMarkObj)
1994 return false;
1997 if (nChgMarkNum!=SAL_MAX_SIZE)
1999 GetMarkedObjectListWriteAccess().DeleteMark(nChgMarkNum);
2001 MarkObj(pMarkObj,pPageView); // also calls MarkListHasChanged(), AdjustMarkHdl()
2002 return true;
2005 bool SdrMarkView::MarkNextObj(const Point& rPnt, short nTol, bool bPrev)
2007 SortMarkedObjects();
2008 nTol=ImpGetHitTolLogic(nTol,nullptr);
2009 SdrMark* pTopMarkHit=nullptr;
2010 SdrMark* pBtmMarkHit=nullptr;
2011 size_t nTopMarkHit=0;
2012 size_t nBtmMarkHit=0;
2013 // find topmost of the selected objects that is hit by rPnt
2014 const size_t nMarkCount=GetMarkedObjectCount();
2015 for (size_t nm=nMarkCount; nm>0 && pTopMarkHit==nullptr;) {
2016 --nm;
2017 SdrMark* pM=GetSdrMarkByIndex(nm);
2018 if(CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr))
2020 pTopMarkHit=pM;
2021 nTopMarkHit=nm;
2024 // nothing found, in this case, just select an object
2025 if (pTopMarkHit==nullptr) return MarkObj(rPnt,sal_uInt16(nTol));
2027 SdrObject* pTopObjHit=pTopMarkHit->GetMarkedSdrObj();
2028 SdrObjList* pObjList=pTopObjHit->getParentSdrObjListFromSdrObject();
2029 SdrPageView* pPV=pTopMarkHit->GetPageView();
2030 // find lowermost of the selected objects that is hit by rPnt
2031 // and is placed on the same PageView as pTopMarkHit
2032 for (size_t nm=0; nm<nMarkCount && pBtmMarkHit==nullptr; ++nm) {
2033 SdrMark* pM=GetSdrMarkByIndex(nm);
2034 SdrPageView* pPV2=pM->GetPageView();
2035 if (pPV2==pPV && CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pPV2,SdrSearchOptions::NONE,nullptr))
2037 pBtmMarkHit=pM;
2038 nBtmMarkHit=nm;
2041 if (pBtmMarkHit==nullptr) { pBtmMarkHit=pTopMarkHit; nBtmMarkHit=nTopMarkHit; }
2042 SdrObject* pBtmObjHit=pBtmMarkHit->GetMarkedSdrObj();
2043 const size_t nObjCount = pObjList->GetObjCount();
2045 size_t nSearchBeg(0);
2046 E3dScene* pScene(nullptr);
2047 SdrObject* pObjHit(bPrev ? pBtmObjHit : pTopObjHit);
2048 bool bRemap =
2049 nullptr != dynamic_cast< const E3dCompoundObject* >(pObjHit);
2050 if (bRemap)
2052 pScene = DynCastE3dScene(pObjHit->getParentSdrObjectFromSdrObject());
2053 bRemap = nullptr != pScene;
2056 if(bPrev)
2058 sal_uInt32 nOrdNumBtm(pBtmObjHit->GetOrdNum());
2060 if(bRemap)
2062 nOrdNumBtm = pScene->RemapOrdNum(nOrdNumBtm);
2065 nSearchBeg = nOrdNumBtm + 1;
2067 else
2069 sal_uInt32 nOrdNumTop(pTopObjHit->GetOrdNum());
2071 if(bRemap)
2073 nOrdNumTop = pScene->RemapOrdNum(nOrdNumTop);
2076 nSearchBeg = nOrdNumTop;
2079 size_t no=nSearchBeg;
2080 SdrObject* pFndObj=nullptr;
2081 while (pFndObj==nullptr && ((!bPrev && no>0) || (bPrev && no<nObjCount))) {
2082 if (!bPrev) no--;
2083 SdrObject* pObj;
2085 if(bRemap)
2087 pObj = pObjList->GetObj(pScene->RemapOrdNum(no));
2089 else
2091 pObj = pObjList->GetObj(no);
2094 if (CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr))
2096 if (TryToFindMarkedObject(pObj)==SAL_MAX_SIZE) {
2097 pFndObj=pObj;
2098 } else {
2099 // TODO: for performance reasons set on to Top or Btm, if necessary
2102 if (bPrev) no++;
2104 if (pFndObj!=nullptr)
2106 GetMarkedObjectListWriteAccess().DeleteMark(bPrev?nBtmMarkHit:nTopMarkHit);
2107 GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pFndObj,pPV));
2108 MarkListHasChanged();
2109 AdjustMarkHdl();
2111 return pFndObj!=nullptr;
2114 void SdrMarkView::MarkObj(const tools::Rectangle& rRect, bool bUnmark)
2116 bool bFnd=false;
2117 tools::Rectangle aR(rRect);
2118 SdrObjList* pObjList;
2119 BrkAction();
2120 SdrPageView* pPV = GetSdrPageView();
2122 if(pPV)
2124 pObjList=pPV->GetObjList();
2125 tools::Rectangle aFrm1(aR);
2126 const size_t nObjCount = pObjList->GetObjCount();
2127 for (size_t nO=0; nO<nObjCount; ++nO) {
2128 SdrObject* pObj=pObjList->GetObj(nO);
2129 tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2130 if (aFrm1.Contains(aRect)) {
2131 if (!bUnmark) {
2132 if (IsObjMarkable(pObj,pPV))
2134 GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
2135 bFnd=true;
2137 } else {
2138 const size_t nPos=TryToFindMarkedObject(pObj);
2139 if (nPos!=SAL_MAX_SIZE)
2141 GetMarkedObjectListWriteAccess().DeleteMark(nPos);
2142 bFnd=true;
2148 if (bFnd) {
2149 SortMarkedObjects();
2150 MarkListHasChanged();
2151 AdjustMarkHdl();
2155 namespace {
2157 void collectUIInformation(const SdrObject* pObj)
2159 EventDescription aDescription;
2160 aDescription.aAction = "SELECT";
2161 aDescription.aParent = "MainWindow";
2162 aDescription.aKeyWord = "CurrentApp";
2164 if (!pObj->GetName().isEmpty())
2165 aDescription.aParameters = {{"OBJECT", pObj->GetName()}};
2166 else
2167 aDescription.aParameters = {{"OBJECT", "Unnamed_Obj_" + OUString::number(pObj->GetOrdNum())}};
2169 UITestLogger::getInstance().logEvent(aDescription);
2174 void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bDoNoSetMarkHdl,
2175 std::vector<basegfx::B2DRectangle> && rSubSelections)
2177 if (!(pObj!=nullptr && pPV!=nullptr && IsObjMarkable(pObj, pPV)))
2178 return;
2180 BrkAction();
2181 if (!bUnmark)
2183 GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
2184 collectUIInformation(pObj);
2186 else
2188 const size_t nPos=TryToFindMarkedObject(pObj);
2189 if (nPos!=SAL_MAX_SIZE)
2191 GetMarkedObjectListWriteAccess().DeleteMark(nPos);
2195 maSubSelectionList = std::move(rSubSelections);
2197 if (!bDoNoSetMarkHdl) {
2198 MarkListHasChanged();
2199 AdjustMarkHdl();
2203 bool SdrMarkView::IsObjMarked(SdrObject const * pObj) const
2205 return TryToFindMarkedObject(pObj)!=SAL_MAX_SIZE;
2208 sal_uInt16 SdrMarkView::GetMarkHdlSizePixel() const
2210 return maHdlList.GetHdlSize()*2+1;
2213 void SdrMarkView::SetMarkHdlSizePixel(sal_uInt16 nSiz)
2215 if (nSiz<3) nSiz=3;
2216 nSiz/=2;
2217 if (nSiz!=maHdlList.GetHdlSize()) {
2218 maHdlList.SetHdlSize(nSiz);
2222 bool SdrMarkView::getPossibleGridOffsetForSdrObject(
2223 basegfx::B2DVector& rOffset,
2224 const SdrObject* pObj,
2225 const SdrPageView* pPV) const
2227 if(nullptr == pObj || nullptr == pPV)
2229 return false;
2232 const OutputDevice* pOutputDevice(GetFirstOutputDevice());
2234 if(nullptr == pOutputDevice)
2236 return false;
2239 const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
2241 if(nullptr == pSdrPageWindow)
2243 return false;
2246 const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
2248 if(!rObjectContact.supportsGridOffsets())
2250 return false;
2253 const sdr::contact::ViewObjectContact& rVOC(pObj->GetViewContact().GetViewObjectContact(
2254 const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
2256 rOffset = rVOC.getGridOffset();
2258 return !rOffset.equalZero();
2261 bool SdrMarkView::getPossibleGridOffsetForPosition(
2262 basegfx::B2DVector& rOffset,
2263 const basegfx::B2DPoint& rPoint,
2264 const SdrPageView* pPV) const
2266 if(nullptr == pPV)
2268 return false;
2271 const OutputDevice* pOutputDevice(GetFirstOutputDevice());
2273 if(nullptr == pOutputDevice)
2275 return false;
2278 const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
2280 if(nullptr == pSdrPageWindow)
2282 return false;
2285 const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
2287 if(!rObjectContact.supportsGridOffsets())
2289 return false;
2292 rObjectContact.calculateGridOffsetForB2DRange(rOffset, basegfx::B2DRange(rPoint));
2294 return !rOffset.equalZero();
2297 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const
2299 if(((nOptions & SdrSearchOptions::IMPISMASTER) && pObj->IsNotVisibleAsMaster()) || (!pObj->IsVisible()))
2301 return nullptr;
2304 const bool bCheckIfMarkable(nOptions & SdrSearchOptions::TESTMARKABLE);
2305 const bool bDeep(nOptions & SdrSearchOptions::DEEP);
2306 const bool bOLE(dynamic_cast< const SdrOle2Obj* >(pObj) != nullptr);
2307 auto pTextObj = DynCastSdrTextObj( pObj);
2308 const bool bTXT(pTextObj && pTextObj->IsTextFrame());
2309 SdrObject* pRet=nullptr;
2310 tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2312 // add possible GridOffset to up-to-now view-independent BoundRect data
2313 basegfx::B2DVector aGridOffset(0.0, 0.0);
2314 if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, pPV))
2316 aRect += Point(
2317 basegfx::fround(aGridOffset.getX()),
2318 basegfx::fround(aGridOffset.getY()));
2321 double nTol2(nTol);
2323 // double tolerance for OLE, text frames and objects in
2324 // active text edit
2325 if(bOLE || bTXT || pObj==static_cast<const SdrObjEditView*>(this)->GetTextEditObject())
2327 nTol2*=2;
2330 aRect.AdjustLeft( -nTol2 ); // add 1 tolerance for all objects
2331 aRect.AdjustTop( -nTol2 );
2332 aRect.AdjustRight(nTol2 );
2333 aRect.AdjustBottom(nTol2 );
2335 if (aRect.Contains(rPnt))
2337 if (!bCheckIfMarkable || IsObjMarkable(pObj,pPV))
2339 SdrObjList* pOL=pObj->GetSubList();
2341 if (pOL!=nullptr && pOL->GetObjCount()!=0)
2343 SdrObject* pTmpObj;
2344 // adjustment hit point for virtual objects
2345 Point aPnt( rPnt );
2347 if ( auto pVirtObj = dynamic_cast<const SdrVirtObj*>( pObj) )
2349 Point aOffset = pVirtObj->GetOffset();
2350 aPnt.Move( -aOffset.X(), -aOffset.Y() );
2353 pRet=CheckSingleSdrObjectHit(aPnt,nTol,pOL,pPV,nOptions,pMVisLay,pTmpObj);
2355 else
2357 if(!pMVisLay || pMVisLay->IsSet(pObj->GetLayer()))
2359 pRet = SdrObjectPrimitiveHit(*pObj, rPnt, {nTol2, nTol2}, *pPV, &pPV->GetVisibleLayers(), false);
2365 if (!bDeep && pRet!=nullptr)
2367 pRet=pObj;
2370 return pRet;
2373 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj) const
2375 return (*this).CheckSingleSdrObjectHit(rPnt,nTol,pOL,pPV,nOptions,pMVisLay,rpRootObj,nullptr);
2377 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj,const SdrMarkList * pMarkList) const
2379 SdrObject* pRet=nullptr;
2380 rpRootObj=nullptr;
2381 if (!pOL)
2382 return nullptr;
2383 const E3dScene* pRemapScene = DynCastE3dScene(pOL->getSdrObjectFromSdrObjList());
2384 const size_t nObjCount(pOL->GetObjCount());
2385 size_t nObjNum(nObjCount);
2387 while (pRet==nullptr && nObjNum>0)
2389 nObjNum--;
2390 SdrObject* pObj;
2392 if(pRemapScene)
2394 pObj = pOL->GetObj(pRemapScene->RemapOrdNum(nObjNum));
2396 else
2398 pObj = pOL->GetObj(nObjNum);
2400 if (nOptions & SdrSearchOptions::BEFOREMARK)
2402 if (pMarkList!=nullptr)
2404 if ((*pMarkList).FindObject(pObj)!=SAL_MAX_SIZE)
2406 return nullptr;
2410 pRet=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,pMVisLay);
2411 if (pRet!=nullptr) rpRootObj=pObj;
2413 return pRet;
2416 SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
2418 return PickObj(rPnt, nTol, rpPV, nOptions, nullptr);
2421 SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions, SdrObject** ppRootObj, bool* pbHitPassDirect) const
2422 { // TODO: lacks a Pass2,Pass3
2423 SortMarkedObjects();
2424 if (ppRootObj!=nullptr) *ppRootObj=nullptr;
2425 if (pbHitPassDirect!=nullptr) *pbHitPassDirect=true;
2426 SdrObject* pRet = nullptr;
2427 rpPV=nullptr;
2428 bool bMarked(nOptions & SdrSearchOptions::MARKED);
2429 bool bMasters=!bMarked && bool(nOptions & SdrSearchOptions::ALSOONMASTER);
2430 // nOptions & SdrSearchOptions::NEXT: n.i.
2431 // nOptions & SdrSearchOptions::PASS2BOUND: n.i.
2432 // nOptions & SdrSearchOptions::PASS3NEAREST// n.i.
2433 if (nTol<0) nTol=ImpGetHitTolLogic(nTol,nullptr);
2434 SdrObject* pObj=nullptr;
2435 SdrObject* pHitObj=nullptr;
2436 SdrPageView* pPV=nullptr;
2437 if (static_cast<const SdrObjEditView*>(this)->IsTextEditFrameHit(rPnt)) {
2438 pObj=static_cast<const SdrObjEditView*>(this)->GetTextEditObject();
2439 pHitObj=pObj;
2440 pPV=static_cast<const SdrObjEditView*>(this)->GetTextEditPageView();
2442 if (bMarked) {
2443 const size_t nMrkCnt=GetMarkedObjectCount();
2444 size_t nMrkNum=nMrkCnt;
2445 while (pHitObj==nullptr && nMrkNum>0) {
2446 nMrkNum--;
2447 SdrMark* pM=GetSdrMarkByIndex(nMrkNum);
2448 pObj=pM->GetMarkedSdrObj();
2449 pPV=pM->GetPageView();
2450 pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,nullptr);
2453 else
2455 pPV = GetSdrPageView();
2457 if(pPV)
2459 SdrPage* pPage=pPV->GetPage();
2460 sal_uInt16 nPgCount=1;
2462 if(bMasters && pPage->TRG_HasMasterPage())
2464 nPgCount++;
2466 bool bWholePage(nOptions & SdrSearchOptions::WHOLEPAGE);
2467 bool bExtraPassForWholePage=bWholePage && pPage!=pPV->GetObjList();
2468 if (bExtraPassForWholePage) nPgCount++; // First search in AktObjList, then on the entire page
2469 sal_uInt16 nPgNum=nPgCount;
2470 while (pHitObj==nullptr && nPgNum>0) {
2471 SdrSearchOptions nTmpOptions=nOptions;
2472 nPgNum--;
2473 const SdrLayerIDSet* pMVisLay=nullptr;
2474 SdrObjList* pObjList=nullptr;
2475 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = true;
2476 if (nPgNum>=nPgCount-1 || (bExtraPassForWholePage && nPgNum>=nPgCount-2))
2478 pObjList=pPV->GetObjList();
2479 if (bExtraPassForWholePage && nPgNum==nPgCount-2) {
2480 pObjList=pPage;
2481 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
2484 else
2486 // otherwise MasterPage
2487 SdrPage& rMasterPage = pPage->TRG_GetMasterPage();
2488 pMVisLay = &pPage->TRG_GetMasterPageVisibleLayers();
2489 pObjList = &rMasterPage;
2491 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
2492 nTmpOptions=nTmpOptions | SdrSearchOptions::IMPISMASTER;
2494 pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObjList,pPV,nTmpOptions,pMVisLay,pObj,&(GetMarkedObjectList()));
2498 if (pHitObj!=nullptr) {
2499 if (ppRootObj!=nullptr) *ppRootObj=pObj;
2500 if (nOptions & SdrSearchOptions::DEEP) pObj=pHitObj;
2501 if (nOptions & SdrSearchOptions::TESTTEXTEDIT) {
2502 if (!pObj->HasTextEdit() || pPV->GetLockedLayers().IsSet(pObj->GetLayer())) {
2503 pObj=nullptr;
2506 if (pObj!=nullptr && (nOptions & SdrSearchOptions::TESTMACRO)) {
2507 SdrObjMacroHitRec aHitRec;
2508 aHitRec.aPos=rPnt;
2509 aHitRec.nTol=nTol;
2510 aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
2511 aHitRec.pPageView=pPV;
2512 if (!pObj->HasMacro() || !pObj->IsMacroHit(aHitRec)) pObj=nullptr;
2514 if (pObj!=nullptr) {
2515 pRet=pObj;
2516 rpPV=pPV;
2519 return pRet;
2522 bool SdrMarkView::PickMarkedObj(const Point& rPnt, SdrObject*& rpObj, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
2524 SortMarkedObjects();
2525 const bool bBoundCheckOn2ndPass(nOptions & SdrSearchOptions::PASS2BOUND);
2526 rpObj=nullptr;
2527 rpPV=nullptr;
2528 const size_t nMarkCount=GetMarkedObjectCount();
2529 for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
2530 --nMarkNum;
2531 SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
2532 SdrPageView* pPV=pM->GetPageView();
2533 SdrObject* pObj=pM->GetMarkedSdrObj();
2534 if (CheckSingleSdrObjectHit(rPnt,mnHitTolLog,pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr)) {
2535 rpObj=pObj;
2536 rpPV=pPV;
2537 return true;
2540 if (bBoundCheckOn2ndPass) {
2541 for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
2542 --nMarkNum;
2543 SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
2544 SdrPageView* pPV=pM->GetPageView();
2545 SdrObject* pObj=pM->GetMarkedSdrObj();
2546 tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2547 aRect.AdjustLeft( -mnHitTolLog );
2548 aRect.AdjustTop( -mnHitTolLog );
2549 aRect.AdjustRight(mnHitTolLog );
2550 aRect.AdjustBottom(mnHitTolLog );
2551 if (aRect.Contains(rPnt)) {
2552 rpObj=pObj;
2553 rpPV=pPV;
2554 return true;
2558 return false;
2562 void SdrMarkView::UnmarkAllObj(SdrPageView const * pPV)
2564 if (GetMarkedObjectCount()==0)
2565 return;
2567 BrkAction();
2568 if (pPV!=nullptr)
2570 GetMarkedObjectListWriteAccess().DeletePageView(*pPV);
2572 else
2574 GetMarkedObjectListWriteAccess().Clear();
2576 mpMarkedObj=nullptr;
2577 mpMarkedPV=nullptr;
2578 MarkListHasChanged();
2579 AdjustMarkHdl();
2582 void SdrMarkView::MarkAllObj(SdrPageView* pPV)
2584 BrkAction();
2586 if(!pPV)
2588 pPV = GetSdrPageView();
2591 // #i69171# pPV may still be NULL if there is no SDrPageView (!), e.g. when inserting
2592 // other files
2593 if(pPV)
2595 const bool bMarkChg(GetMarkedObjectListWriteAccess().InsertPageView(*pPV));
2597 if(bMarkChg)
2599 MarkListHasChanged();
2603 if(GetMarkedObjectCount())
2605 AdjustMarkHdl();
2609 void SdrMarkView::AdjustMarkHdl(SfxViewShell* pOtherShell)
2611 CheckMarked();
2612 SetMarkRects();
2613 SetMarkHandles(pOtherShell);
2616 // BoundRect in model coordinates, no GridOffset added
2617 tools::Rectangle SdrMarkView::GetMarkedObjBoundRect() const
2619 tools::Rectangle aRect;
2620 for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
2621 SdrMark* pM=GetSdrMarkByIndex(nm);
2622 SdrObject* pO=pM->GetMarkedSdrObj();
2623 tools::Rectangle aR1(pO->GetCurrentBoundRect());
2624 if (aRect.IsEmpty()) aRect=aR1;
2625 else aRect.Union(aR1);
2627 return aRect;
2630 // ObjRect in model coordinates, no GridOffset added
2631 const tools::Rectangle& SdrMarkView::GetMarkedObjRect() const
2633 if (mbMarkedObjRectDirty) {
2634 const_cast<SdrMarkView*>(this)->mbMarkedObjRectDirty=false;
2635 tools::Rectangle aRect;
2636 for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
2637 SdrMark* pM=GetSdrMarkByIndex(nm);
2638 SdrObject* pO = pM->GetMarkedSdrObj();
2639 if (!pO)
2640 continue;
2641 tools::Rectangle aR1(pO->GetSnapRect());
2642 if (aRect.IsEmpty()) aRect=aR1;
2643 else aRect.Union(aR1);
2645 const_cast<SdrMarkView*>(this)->maMarkedObjRect=aRect;
2647 return maMarkedObjRect;
2651 OUString SdrMarkView::ImpGetDescriptionString(TranslateId pStrCacheID, ImpGetDescriptionOptions nOpt) const
2653 OUString sStr = SvxResId(pStrCacheID);
2654 const sal_Int32 nPos = sStr.indexOf("%1");
2656 if(nPos != -1)
2658 if(nOpt == ImpGetDescriptionOptions::POINTS)
2660 sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedPoints());
2662 else if(nOpt == ImpGetDescriptionOptions::GLUEPOINTS)
2664 sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedGluePoints());
2666 else
2668 sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedObjects());
2672 return sStr.replaceFirst("%2", "0");
2676 void SdrMarkView::EnterMarkedGroup()
2678 // We enter only the first group found (in only one PageView), because
2679 // PageView::EnterGroup calls an AdjustMarkHdl.
2680 // TODO: I'll have to prevent that via a flag.
2681 SdrPageView* pPV = GetSdrPageView();
2683 if(!pPV)
2684 return;
2686 bool bEnter=false;
2687 for (size_t nm = GetMarkedObjectCount(); nm > 0 && !bEnter;)
2689 --nm;
2690 SdrMark* pM=GetSdrMarkByIndex(nm);
2691 if (pM->GetPageView()==pPV) {
2692 SdrObject* pObj=pM->GetMarkedSdrObj();
2693 if (pObj->IsGroupObject()) {
2694 if (pPV->EnterGroup(pObj)) {
2695 bEnter=true;
2703 void SdrMarkView::MarkListHasChanged()
2705 GetMarkedObjectListWriteAccess().SetNameDirty();
2706 maSdrViewSelection.SetEdgesOfMarkedNodesDirty();
2708 mbMarkedObjRectDirty=true;
2709 mbMarkedPointsRectsDirty=true;
2710 bool bOneEdgeMarked=false;
2711 if (GetMarkedObjectCount()==1) {
2712 const SdrObject* pObj=GetMarkedObjectByIndex(0);
2713 if (pObj->GetObjInventor()==SdrInventor::Default) {
2714 bOneEdgeMarked = pObj->GetObjIdentifier() == SdrObjKind::Edge;
2717 ImpSetGlueVisible4(bOneEdgeMarked);
2721 void SdrMarkView::SetMoveOutside(bool bOn)
2723 maHdlList.SetMoveOutside(bOn);
2726 void SdrMarkView::SetDesignMode( bool bOn )
2728 if ( mbDesignMode != bOn )
2730 mbDesignMode = bOn;
2731 SdrPageView* pPageView = GetSdrPageView();
2732 if ( pPageView )
2733 pPageView->SetDesignMode( bOn );
2737 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */