Bump version to 24.04.3.4
[LibreOffice.git] / svx / source / svdraw / svdmrkv.cxx
blob38e13d8e385792bbfb0e626c628b2e954d7485da
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/displayinfo.hxx>
44 #include <svx/sdr/contact/objectcontact.hxx>
45 #include <svx/sdr/overlay/overlaymanager.hxx>
46 #include <svx/sdr/overlay/overlayselection.hxx>
47 #include <svx/sdr/contact/viewcontact.hxx>
48 #include <svx/sdr/contact/viewobjectcontact.hxx>
49 #include <svx/sdrpaintwindow.hxx>
50 #include <svx/sdrpagewindow.hxx>
51 #include <svx/sdrhittesthelper.hxx>
52 #include <vcl/uitest/logger.hxx>
53 #include <vcl/uitest/eventdescription.hxx>
54 #include <vcl/window.hxx>
55 #include <o3tl/string_view.hxx>
57 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
58 #include <comphelper/lok.hxx>
59 #include <sfx2/lokhelper.hxx>
60 #include <sfx2/lokcomponenthelpers.hxx>
61 #include <sfx2/viewsh.hxx>
62 #include <svtools/optionsdrawinglayer.hxx>
64 #include <drawinglayer/processor2d/textextractor2d.hxx>
66 #include <array>
68 #include <com/sun/star/frame/XController.hpp>
69 #include <com/sun/star/view/XSelectionSupplier.hpp>
71 #include <boost/property_tree/json_parser.hpp>
73 using namespace com::sun::star;
75 // Migrate Marking of Objects, Points and GluePoints
77 class ImplMarkingOverlay
79 // The OverlayObjects
80 sdr::overlay::OverlayObjectList maObjects;
82 // The remembered second position in logical coordinates
83 basegfx::B2DPoint maSecondPosition;
85 // A flag to remember if the action is for unmarking.
86 bool mbUnmarking : 1;
88 public:
89 ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking);
91 // The OverlayObjects are cleared using the destructor of OverlayObjectList.
92 // That destructor calls clear() at the list which removes all objects from the
93 // OverlayManager and deletes them.
95 void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
96 bool IsUnmarking() const { return mbUnmarking; }
99 ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
100 : maSecondPosition(rStartPos),
101 mbUnmarking(bUnmarking)
103 if (comphelper::LibreOfficeKit::isActive())
104 return; // We do client-side object manipulation with the Kit API
106 for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
108 SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
109 const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
111 if (xTargetOverlay.is())
113 std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(new sdr::overlay::OverlayRollingRectangleStriped(
114 rStartPos, rStartPos, false));
115 xTargetOverlay->add(*pNew);
116 maObjects.append(std::move(pNew));
121 void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
123 if(rNewPosition != maSecondPosition)
125 // apply to OverlayObjects
126 for(sal_uInt32 a(0); a < maObjects.count(); a++)
128 sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
129 rCandidate.setSecondPosition(rNewPosition);
132 // remember new position
133 maSecondPosition = rNewPosition;
137 class MarkingSubSelectionOverlay
139 sdr::overlay::OverlayObjectList maObjects;
141 public:
142 MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
144 if (comphelper::LibreOfficeKit::isActive())
145 return; // We do client-side object manipulation with the Kit API
147 for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
149 SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
150 const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
152 if (xTargetOverlay.is())
154 const Color aHighlightColor = SvtOptionsDrawinglayer::getHilightColor();
156 std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
157 std::make_unique<sdr::overlay::OverlaySelection>(
158 sdr::overlay::OverlayType::Transparent,
159 aHighlightColor, std::vector(rSelections), false);
161 xTargetOverlay->add(*pNew);
162 maObjects.append(std::move(pNew));
168 SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
169 : SdrSnapView(rSdrModel, pOut)
170 , mpMarkedObj(nullptr)
171 , mpMarkedPV(nullptr)
172 , maHdlList(this)
173 , meDragMode(SdrDragMode::Move)
174 , meEditMode(SdrViewEditMode::Edit)
175 , meEditMode0(SdrViewEditMode::Edit)
176 , mbDesignMode(false)
177 , mbForceFrameHandles(false)
178 , mbPlusHdlAlways(false)
179 , mbInsPolyPoint(false)
180 , mbMarkedObjRectDirty(false)
181 , mbMrkPntDirty(false)
182 , mbMarkedPointsRectsDirty(false)
183 , mbMarkHandlesHidden(false)
184 , mbNegativeX(false)
187 BrkMarkObj();
188 BrkMarkPoints();
189 BrkMarkGluePoints();
191 StartListening(rSdrModel);
194 SdrMarkView::~SdrMarkView()
196 // Migrate selections
197 BrkMarkObj();
198 BrkMarkPoints();
199 BrkMarkGluePoints();
202 void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
204 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
206 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
207 SdrHintKind eKind=pSdrHint->GetKind();
208 if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
210 mbMarkedObjRectDirty=true;
211 mbMarkedPointsRectsDirty=true;
214 SdrSnapView::Notify(rBC,rHint);
217 void SdrMarkView::ModelHasChanged()
219 SdrPaintView::ModelHasChanged();
220 GetMarkedObjectListWriteAccess().SetNameDirty();
221 mbMarkedObjRectDirty=true;
222 mbMarkedPointsRectsDirty=true;
223 // Example: Obj is selected and maMarkedObjectList is sorted.
224 // In another View 2, the ObjOrder is changed (e. g. MovToTop())
225 // Then we need to re-sort MarkList.
226 GetMarkedObjectListWriteAccess().SetUnsorted();
227 SortMarkedObjects();
228 mbMrkPntDirty=true;
229 UndirtyMrkPnt();
230 SdrView* pV=static_cast<SdrView*>(this);
231 if (pV!=nullptr && !pV->IsDragObj() && !pV->IsInsObjPoint()) {
232 AdjustMarkHdl();
235 if (comphelper::LibreOfficeKit::isActive())
236 modelHasChangedLOKit();
239 void SdrMarkView::modelHasChangedLOKit()
241 if (GetMarkedObjectCount() <= 0)
242 return;
244 //TODO: Is MarkedObjRect valid at this point?
245 tools::Rectangle aSelection(GetMarkedObjRect());
246 tools::Rectangle* pResultSelection;
247 if (aSelection.IsEmpty())
248 pResultSelection = nullptr;
249 else
251 sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
252 if (nTotalPaintWindows == 1)
254 const OutputDevice* pOut = this->GetFirstOutputDevice();
255 const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
256 if (pWin && pWin->IsChart())
258 const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
259 if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
261 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
262 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
263 aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
268 // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
269 if (mpMarkedPV)
271 if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
273 if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
274 aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
278 pResultSelection = &aSelection;
280 if (mbNegativeX)
282 // Convert to positive X doc-coordinates
283 tools::Long nTmp = aSelection.Left();
284 aSelection.SetLeft(-aSelection.Right());
285 aSelection.SetRight(-nTmp);
289 if (SfxViewShell* pViewShell = GetSfxViewShell())
290 SfxLokHelper::notifyInvalidation(pViewShell, pResultSelection);
293 bool SdrMarkView::IsAction() const
295 return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
298 void SdrMarkView::MovAction(const Point& rPnt)
300 SdrSnapView::MovAction(rPnt);
302 if(IsMarkObj())
304 MovMarkObj(rPnt);
306 else if(IsMarkPoints())
308 MovMarkPoints(rPnt);
310 else if(IsMarkGluePoints())
312 MovMarkGluePoints(rPnt);
316 void SdrMarkView::EndAction()
318 if(IsMarkObj())
320 EndMarkObj();
322 else if(IsMarkPoints())
324 EndMarkPoints();
326 else if(IsMarkGluePoints())
328 EndMarkGluePoints();
331 SdrSnapView::EndAction();
334 void SdrMarkView::BckAction()
336 SdrSnapView::BckAction();
337 BrkMarkObj();
338 BrkMarkPoints();
339 BrkMarkGluePoints();
342 void SdrMarkView::BrkAction()
344 SdrSnapView::BrkAction();
345 BrkMarkObj();
346 BrkMarkPoints();
347 BrkMarkGluePoints();
350 void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
352 if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
354 rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
356 else
358 SdrSnapView::TakeActionRect(rRect);
363 void SdrMarkView::ClearPageView()
365 UnmarkAllObj();
366 SdrSnapView::ClearPageView();
369 void SdrMarkView::HideSdrPage()
371 bool bMrkChg(false);
373 SdrPageView* pPageView = GetSdrPageView();
374 if (pPageView)
376 // break all creation actions when hiding page (#75081#)
377 BrkAction();
379 // Discard all selections on this page
380 bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
383 SdrSnapView::HideSdrPage();
385 if(bMrkChg)
387 MarkListHasChanged();
388 AdjustMarkHdl();
393 void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
395 BrkAction();
397 DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");
399 basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
400 mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
402 maDragStat.Reset(rPnt);
403 maDragStat.NextPoint();
404 maDragStat.SetMinMove(mnMinMovLog);
407 void SdrMarkView::MovMarkObj(const Point& rPnt)
409 if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
411 maDragStat.NextMove(rPnt);
412 DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
413 basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
414 mpMarkObjOverlay->SetSecondPosition(aNewPos);
418 bool SdrMarkView::EndMarkObj()
420 bool bRetval(false);
422 if(IsMarkObj())
424 if(maDragStat.IsMinMoved())
426 tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
427 aRect.Normalize();
428 MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
429 bRetval = true;
432 // cleanup
433 BrkMarkObj();
436 return bRetval;
439 void SdrMarkView::BrkMarkObj()
441 if(IsMarkObj())
443 DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
444 mpMarkObjOverlay.reset();
449 bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
451 if(HasMarkablePoints())
453 BrkAction();
455 DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
456 basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
457 mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
459 maDragStat.Reset(rPnt);
460 maDragStat.NextPoint();
461 maDragStat.SetMinMove(mnMinMovLog);
463 return true;
466 return false;
469 void SdrMarkView::MovMarkPoints(const Point& rPnt)
471 if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
473 maDragStat.NextMove(rPnt);
475 DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
476 basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
477 mpMarkPointsOverlay->SetSecondPosition(aNewPos);
481 bool SdrMarkView::EndMarkPoints()
483 bool bRetval(false);
485 if(IsMarkPoints())
487 if(maDragStat.IsMinMoved())
489 tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
490 aRect.Normalize();
491 MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());
493 bRetval = true;
496 // cleanup
497 BrkMarkPoints();
500 return bRetval;
503 void SdrMarkView::BrkMarkPoints()
505 if(IsMarkPoints())
507 DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
508 mpMarkPointsOverlay.reset();
513 bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
515 if(HasMarkableGluePoints())
517 BrkAction();
519 DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");
521 basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
522 mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
523 maDragStat.Reset(rPnt);
524 maDragStat.NextPoint();
525 maDragStat.SetMinMove(mnMinMovLog);
527 return true;
530 return false;
533 void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
535 if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
537 maDragStat.NextMove(rPnt);
539 DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
540 basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
541 mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
545 void SdrMarkView::EndMarkGluePoints()
547 if(IsMarkGluePoints())
549 if(maDragStat.IsMinMoved())
551 tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
552 aRect.Normalize();
553 MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
556 // cleanup
557 BrkMarkGluePoints();
561 void SdrMarkView::BrkMarkGluePoints()
563 if(IsMarkGluePoints())
565 DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
566 mpMarkGluePointsOverlay.reset();
570 bool SdrMarkView::MarkableObjectsExceed( int n ) const
572 SdrPageView* pPV = GetSdrPageView();
573 if (!pPV)
574 return false;
576 SdrObjList* pOL=pPV->GetObjList();
577 for (const rtl::Reference<SdrObject>& pObj : *pOL)
578 if (IsObjMarkable(pObj.get(),pPV) && --n<0)
579 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 boost::property_tree::ptree elements;
715 for (const rtl::Reference<SdrObject>& pObj : *pOL)
717 if (!pObj)
718 continue;
719 if (pObj == GetMarkedObjectByIndex(0))
720 continue;
721 const SdrGluePointList* pGPL = pObj->GetGluePointList();
722 bool VertexObject = !(pGPL && pGPL->GetCount());
723 const size_t count = !VertexObject ? pGPL->GetCount() : 4;
724 boost::property_tree::ptree object;
725 boost::property_tree::ptree points;
726 for (size_t i = 0; i < count; ++i)
728 boost::property_tree::ptree node;
729 boost::property_tree::ptree point;
730 const SdrGluePoint& rGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
731 Point rPoint = rGP.GetAbsolutePos(*pObj);
732 if (bConvertUnit)
734 rPoint = o3tl::convert(rPoint, o3tl::Length::mm100, o3tl::Length::twip);
736 point.put("x", nSignX * rPoint.getX());
737 point.put("y", rPoint.getY());
738 node.add_child("point", point);
739 points.push_back(std::make_pair("", node));
741 basegfx::B2DVector aGridOffset(0.0, 0.0);
742 Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
743 if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
745 Point p(aGridOffset.getX(), aGridOffset.getY());
746 if (bConvertUnit)
748 p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
750 boost::property_tree::ptree gridOffset;
751 gridOffset.put("x", nSignX * p.getX());
752 gridOffset.put("y", p.getY());
753 object.add_child("gridoffset", gridOffset);
755 object.put("ordnum", pObj->GetOrdNum());
756 object.add_child("gluepoints", points);
757 elements.push_back(std::make_pair("", object));
758 result = true;
760 rTree.add_child("shapes", elements);
762 return result;
765 namespace
767 class TextBoundsExtractor final : public drawinglayer::processor2d::TextExtractor2D
769 private:
770 basegfx::B2DRange maTextRange;
771 void processTextPrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override
773 maTextRange.expand(rCandidate.getB2DRange(getViewInformation2D()));
775 public:
776 explicit TextBoundsExtractor(const drawinglayer::geometry::ViewInformation2D& rViewInformation)
777 : drawinglayer::processor2d::TextExtractor2D(rViewInformation, true)
781 basegfx::B2DRange getTextBounds(const sdr::contact::ViewObjectContact &rVOC, sdr::contact::DisplayInfo &raDisplayInfo)
783 this->process(rVOC.getPrimitive2DSequence(raDisplayInfo));
784 return maTextRange;
789 OString SdrMarkView::CreateInnerTextRectString() const
791 if (!mpMarkedObj)
792 return OString();
794 SdrPageView* pPageView = GetSdrPageView();
795 const sdr::contact::ViewObjectContact& rVOC = mpMarkedObj->GetViewContact().GetViewObjectContact(
796 pPageView->GetPageWindow(0)->GetObjectContact());
798 sdr::contact::DisplayInfo aDisplayInfo;
799 TextBoundsExtractor aTextBoundsExtractor(rVOC.GetObjectContact().getViewInformation2D());
800 basegfx::B2DRange aRange = aTextBoundsExtractor.getTextBounds(rVOC, aDisplayInfo);
801 if (!aRange.isEmpty()) {
802 tools::Rectangle rect(aRange.getMinX(), aRange.getMinY(), aRange.getMaxX(), aRange.getMaxY());
803 tools::Rectangle aRangeTWIP = o3tl::convert(rect, o3tl::Length::mm100, o3tl::Length::twip);
804 OString innerTextInfo = "\"innerTextRect\":[" +
805 OString::number(aRangeTWIP.getX()) + "," +
806 OString::number(aRangeTWIP.getY()) + "," +
807 OString::number(aRangeTWIP.GetWidth()) + "," +
808 OString::number(aRangeTWIP.GetHeight()) + "]";
809 return innerTextInfo;
812 return OString();
815 void SdrMarkView::SetInnerTextAreaForLOKit() const
817 if (!comphelper::LibreOfficeKit::isActive())
818 return;
819 SfxViewShell* pViewShell = GetSfxViewShell();
820 OString sRectString = CreateInnerTextRectString();
821 if (pViewShell && !sRectString.isEmpty())
822 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SHAPE_INNER_TEXT, sRectString);
825 void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
827 SfxViewShell* pViewShell = GetSfxViewShell();
829 tools::Rectangle aSelection(rRect);
830 tools::Long nSignX = mbNegativeX ? -1 : 1;
831 bool bIsChart = false;
832 Point addLogicOffset(0, 0);
833 bool convertMapMode = false;
834 if (!rRect.IsEmpty())
836 sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
837 if (nTotalPaintWindows == 1)
839 const OutputDevice* pOut = this->GetFirstOutputDevice();
840 const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
841 if (pWin && pWin->IsChart())
843 bIsChart = true;
844 const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
845 if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
847 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
848 if (mbNegativeX && AllSettings::GetLayoutRTL())
850 // mbNegativeX is set only for Calc in RTL mode.
851 // If global RTL flag is set, vcl-window X offset of chart window is
852 // mirrored w.r.t parent window rectangle. This needs to be reverted.
853 aOffsetPx.setX(pViewShellWindow->GetOutOffXPixel() + pViewShellWindow->GetSizePixel().Width()
854 - pWin->GetOutOffXPixel() - pWin->GetSizePixel().Width());
856 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
857 addLogicOffset = aLogicOffset;
858 aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
864 if (!aSelection.IsEmpty())
866 // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
867 if (mpMarkedPV)
869 if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
871 if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
873 aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
874 convertMapMode = true;
879 // hide the text selection too
880 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
884 OStringBuffer aExtraInfo;
885 OString sSelectionText;
886 OString sSelectionTextView;
887 boost::property_tree::ptree aTableJsonTree;
888 boost::property_tree::ptree aGluePointsTree;
889 const bool bMediaObj = (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Media);
890 bool bTableSelection = false;
891 bool bConnectorSelection = false;
893 if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Table)
895 auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
896 bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
898 if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Edge)
900 bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
903 SdrPageView* pPageView = GetSdrPageView();
905 if (GetMarkedObjectCount())
907 SdrMark* pM = GetSdrMarkByIndex(0);
908 SdrObject* pO = pM->GetMarkedSdrObj();
909 Degree100 nRotAngle = pO->GetRotateAngle();
910 // true if we are dealing with a RotGrfFlyFrame
911 // (SwVirtFlyDrawObj with a SwGrfNode)
912 bool bWriterGraphic = pO->HasLimitedRotation();
914 OString handleArrayStr;
916 aExtraInfo.append("{\"id\":\""
917 + OString::number(reinterpret_cast<sal_IntPtr>(pO))
918 + "\",\"type\":"
919 + OString::number(static_cast<sal_Int32>(pO->GetObjIdentifier())));
921 if (mpMarkedObj && !pOtherShell)
923 OString innerTextInfo = CreateInnerTextRectString();
924 if (!innerTextInfo.isEmpty())
925 aExtraInfo.append("," + innerTextInfo);
928 // In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
929 // In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
930 // SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
931 // but the rotation is not applied to the LogicRect. Therefore,
932 // what we calculate in online does not match with the core in case of the rotation.
933 // Here we can send the correct gridOffset in the selection callback, this way
934 // whether the shape is rotated or not, we will always have the correct gridOffset
935 // Note that the gridOffset is calculated from the first selected obj
936 basegfx::B2DVector aGridOffset(0.0, 0.0);
937 if(getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), pPageView))
939 Point p(aGridOffset.getX(), aGridOffset.getY());
940 if (convertMapMode)
941 p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
942 aExtraInfo.append(",\"gridOffsetX\":"
943 + OString::number(nSignX * p.getX())
944 + ",\"gridOffsetY\":"
945 + OString::number(p.getY()));
948 if (bWriterGraphic)
950 aExtraInfo.append(", \"isWriterGraphic\": true");
952 else if (bIsChart)
954 LokChartHelper aChartHelper(pViewShell);
955 css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
956 css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
957 if (xSelectionSupplier.is())
959 uno::Any aSel = xSelectionSupplier->getSelection();
960 OUString aValue;
961 if (aSel >>= aValue)
963 OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
964 const std::vector<OString> aProps{"Draggable"_ostr, "Resizable"_ostr, "Rotatable"_ostr};
965 for (const auto& rProp: aProps)
967 sal_Int32 nPos = aObjectCID.indexOf(rProp);
968 if (nPos == -1) continue;
969 nPos += rProp.getLength() + 1; // '='
970 if (aExtraInfo.getLength() > 2) // != "{ "
971 aExtraInfo.append(", ");
972 aExtraInfo.append("\"is" + rProp + "\": "
973 + OString::boolean(aObjectCID[nPos] == '1'));
976 std::u16string_view sDragMethod = lcl_getDragMethodServiceName(aValue);
977 if (sDragMethod == u"PieSegmentDragging")
979 // old initial offset inside the CID returned by xSelectionSupplier->getSelection()
980 // after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
981 aValue = pO->GetName();
982 std::u16string_view sDragParameters = lcl_getDragParameterString(aValue);
983 if (!sDragParameters.empty())
985 aExtraInfo.append(", \"dragInfo\": { "
986 "\"dragMethod\": \""
987 + OUString(sDragMethod).toUtf8()
988 + "\"");
990 sal_Int32 nStartIndex = 0;
991 std::array<int, 5> aDragParameters;
992 for (auto& rParam : aDragParameters)
994 std::u16string_view sParam = o3tl::getToken(sDragParameters, 0, ',', nStartIndex);
995 if (sParam.empty())
996 break;
997 rParam = o3tl::toInt32(sParam);
1000 // initial offset in %
1001 if (aDragParameters[0] < 0)
1002 aDragParameters[0] = 0;
1003 else if (aDragParameters[0] > 100)
1004 aDragParameters[0] = 100;
1006 aExtraInfo.append(", \"initialOffset\": "
1007 + OString::number(static_cast<sal_Int32>(aDragParameters[0])));
1009 // drag direction constraint
1010 Point aMinPos(aDragParameters[1], aDragParameters[2]);
1011 Point aMaxPos(aDragParameters[3], aDragParameters[4]);
1012 Point aDragDirection = aMaxPos - aMinPos;
1013 aDragDirection = o3tl::convert(aDragDirection, o3tl::Length::mm100, o3tl::Length::twip);
1015 aExtraInfo.append(", \"dragDirection\": ["
1016 + aDragDirection.toString()
1017 + "]");
1019 // polygon approximating the pie segment or donut segment
1020 if (pO->GetObjIdentifier() == SdrObjKind::PathFill)
1022 const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
1023 if (aPolyPolygon.count() == 1)
1025 const basegfx::B2DPolygon aPolygon = aPolyPolygon.getB2DPolygon(0);
1026 if (sal_uInt32 nPolySize = aPolygon.count())
1028 const OutputDevice* pOut = this->GetFirstOutputDevice();
1029 const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
1030 const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
1031 if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
1033 // in the following code escaping sequences used inside raw literal strings
1034 // are for making them understandable by the JSON parser
1036 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
1037 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
1038 OString sPolygonElem("<polygon points=\\\""_ostr);
1039 for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
1041 const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
1042 Point aPoint(aB2Point.getX(), aB2Point.getY());
1043 aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
1044 if (mbNegativeX)
1045 aPoint.setX(-aPoint.X());
1046 if (nIndex > 0)
1047 sPolygonElem += " ";
1048 sPolygonElem += aPoint.toString();
1050 sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";
1052 OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
1053 OString::number(aSelection.GetWidth() / 100.0) +
1054 R"elem(mm\" height=\")elem" +
1055 OString::number(aSelection.GetHeight() / 100.0) +
1056 R"elem(mm\" viewBox=\")elem" +
1057 aSelection.toString() +
1058 R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";
1060 aExtraInfo.append(", \"svg\": \""
1061 + sSVGElem
1062 + "\\n "
1063 + sPolygonElem
1064 + "\\n</svg>"
1065 "\""); // svg
1070 aExtraInfo.append("}"); // dragInfo
1076 if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
1078 boost::property_tree::ptree responseJSON;
1079 boost::property_tree::ptree others;
1080 boost::property_tree::ptree anchor;
1081 boost::property_tree::ptree rectangle;
1082 boost::property_tree::ptree poly;
1083 boost::property_tree::ptree custom;
1084 boost::property_tree::ptree nodes;
1085 for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
1087 SdrHdl *pHdl = maHdlList.GetHdl(i);
1088 boost::property_tree::ptree child;
1089 boost::property_tree::ptree point;
1090 sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
1091 child.put("id", pHdl->GetObjHdlNum());
1092 child.put("kind", kind);
1093 child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
1094 Point pHdlPos = pHdl->GetPos();
1095 pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
1096 if (convertMapMode)
1098 pHdlPos = o3tl::convert(pHdlPos, o3tl::Length::mm100, o3tl::Length::twip);
1100 point.put("x", pHdlPos.getX());
1101 point.put("y", pHdlPos.getY());
1102 child.add_child("point", point);
1103 const auto node = std::make_pair("", child);
1104 boost::property_tree::ptree* selectedNode = nullptr;
1105 if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
1107 selectedNode = &rectangle;
1109 else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
1111 selectedNode = &poly;
1113 else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
1115 selectedNode = &custom;
1117 else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
1119 if (getSdrModelFromSdrView().IsWriter())
1120 selectedNode = &anchor;
1121 else
1122 // put it to others as we don't render them except in writer
1123 selectedNode = &others;
1125 else
1127 selectedNode = &others;
1129 std::string sKind = std::to_string(kind);
1130 boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
1131 if (!kindNode)
1133 boost::property_tree::ptree newChild;
1134 newChild.push_back(node);
1135 selectedNode->add_child(sKind.c_str(), newChild);
1137 else
1138 kindNode.get().push_back(node);
1140 nodes.add_child("rectangle", rectangle);
1141 nodes.add_child("poly", poly);
1142 nodes.add_child("custom", custom);
1143 nodes.add_child("anchor", anchor);
1144 nodes.add_child("others", others);
1145 responseJSON.add_child("kinds", nodes);
1146 std::stringstream aStream;
1147 boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
1148 handleArrayStr = ", \"handles\":"_ostr;
1149 handleArrayStr = handleArrayStr + aStream.str().c_str();
1150 if (bConnectorSelection)
1152 aStream.str("");
1153 boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
1154 handleArrayStr = handleArrayStr + ", \"GluePoints\":";
1155 handleArrayStr = handleArrayStr + aStream.str().c_str();
1159 if (mbNegativeX)
1161 tools::Rectangle aNegatedRect(aSelection);
1162 aNegatedRect.SetLeft(-aNegatedRect.Left());
1163 aNegatedRect.SetRight(-aNegatedRect.Right());
1164 aNegatedRect.Normalize();
1165 sSelectionText = aNegatedRect.toString() +
1166 ", " + OString::number(nRotAngle.get());
1168 else
1170 sSelectionText = aSelection.toString() +
1171 ", " + OString::number(nRotAngle.get());
1174 if (!aExtraInfo.isEmpty())
1176 sSelectionTextView = sSelectionText + ", " + aExtraInfo + "}";
1178 if (bMediaObj && pOtherShell == nullptr)
1180 // Add the URL only if we have a Media Object and
1181 // we are the selecting view.
1182 SdrMediaObj* mediaObj = dynamic_cast<SdrMediaObj*>(mpMarkedObj);
1183 if (mediaObj)
1184 aExtraInfo.append(", \"url\": \"" + mediaObj->getTempURL().toUtf8() + "\"");
1187 aExtraInfo.append(handleArrayStr
1188 + "}");
1189 sSelectionText += ", " + aExtraInfo;
1193 if (sSelectionText.isEmpty())
1195 sSelectionText = "EMPTY"_ostr;
1196 sSelectionTextView = "EMPTY"_ostr;
1197 if (!pOtherShell)
1198 pViewShell->NotifyOtherViews(LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection"_ostr, OString());
1201 if (bTableSelection)
1203 boost::property_tree::ptree aTableRectangle;
1204 aTableRectangle.put("x", aSelection.Left());
1205 aTableRectangle.put("y", aSelection.Top());
1206 aTableRectangle.put("width", aSelection.GetWidth());
1207 aTableRectangle.put("height", aSelection.GetHeight());
1208 aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));
1210 std::stringstream aStream;
1211 boost::property_tree::write_json(aStream, aTableJsonTree);
1212 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, OString(aStream.str()));
1214 else if (!getSdrModelFromSdrView().IsWriter())
1216 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}"_ostr);
1219 if (pOtherShell)
1221 // Another shell wants to know about our existing
1222 // selection.
1223 if (pViewShell != pOtherShell)
1224 SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
1226 else
1228 // We have a new selection, so both pViewShell and the
1229 // other views want to know about it.
1230 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText);
1232 SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
1237 void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
1239 // remember old focus handle values to search for it again
1240 const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
1241 bool bSaveOldFocus(false);
1242 sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
1243 SdrHdlKind eSaveKind(SdrHdlKind::Move);
1244 SdrObject* pSaveObj = nullptr;
1246 mpMarkingSubSelectionOverlay.reset();
1248 if(pSaveOldFocusHdl
1249 && pSaveOldFocusHdl->GetObj()
1250 && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
1251 && (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
1253 bSaveOldFocus = true;
1254 nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
1255 nSavePointNum = pSaveOldFocusHdl->GetPointNum();
1256 pSaveObj = pSaveOldFocusHdl->GetObj();
1257 eSaveKind = pSaveOldFocusHdl->GetKind();
1260 // delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
1261 maHdlList.Clear();
1262 maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
1263 maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
1264 mpMarkedObj=nullptr;
1265 mpMarkedPV=nullptr;
1267 // are handles enabled at all? Create only then
1268 if(areMarkHandlesHidden())
1269 return;
1271 // There can be multiple mark views, but we're only interested in the one that has a window associated.
1272 const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;
1274 const size_t nMarkCount=GetMarkedObjectCount();
1275 bool bStdDrag=meDragMode==SdrDragMode::Move;
1276 bool bSingleTextObjMark=false;
1277 bool bLimitedRotation(false);
1279 if (nMarkCount==1)
1281 mpMarkedObj=GetMarkedObjectByIndex(0);
1283 if(nullptr != mpMarkedObj)
1285 bSingleTextObjMark =
1286 DynCastSdrTextObj( mpMarkedObj) != nullptr &&
1287 static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
1289 // RotGrfFlyFrame: we may have limited rotation
1290 bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
1294 bool bFrmHdl=ImpIsFrameHandles();
1296 if (nMarkCount>0)
1298 mpMarkedPV=GetSdrPageViewOfMarkedByIndex(0);
1300 for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
1302 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1304 if (mpMarkedPV!=pM->GetPageView())
1306 mpMarkedPV=nullptr;
1311 SfxViewShell* pViewShell = GetSfxViewShell();
1313 // check if text edit or ole is active and handles need to be suppressed. This may be the case
1314 // when a single object is selected
1315 // Using a strict return statement is okay here; no handles means *no* handles.
1316 if(mpMarkedObj)
1318 // formerly #i33755#: If TextEdit is active the EditEngine will directly paint
1319 // to the window, so suppress Overlay and handles completely; a text frame for
1320 // the active text edit will be painted by the repaint mechanism in
1321 // SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
1322 // in the future
1323 // Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
1324 if(static_cast<SdrView*>(this)->IsTextEdit())
1326 const SdrTextObj* pSdrTextObj = DynCastSdrTextObj(mpMarkedObj);
1328 if (pSdrTextObj && pSdrTextObj->IsInEditMode())
1330 if (!bTiledRendering)
1331 return;
1335 // formerly #i118524#: if inplace activated OLE is selected, suppress handles
1336 const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);
1338 if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
1340 return;
1343 if (!maSubSelectionList.empty())
1345 mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
1349 tools::Rectangle aRect(GetMarkedObjRect());
1351 if (bFrmHdl)
1353 if(!aRect.IsEmpty())
1355 // otherwise nothing is found
1356 const size_t nSiz0(maHdlList.GetHdlCount());
1358 if( bSingleTextObjMark )
1360 mpMarkedObj->AddToHdlList(maHdlList);
1362 else
1364 const bool bWdt0(aRect.Left() == aRect.Right());
1365 const bool bHgt0(aRect.Top() == aRect.Bottom());
1367 if (bWdt0 && bHgt0)
1369 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1371 else if (!bStdDrag && (bWdt0 || bHgt0))
1373 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1374 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
1376 else
1378 if (!bWdt0 && !bHgt0)
1380 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1383 if (!bLimitedRotation && !bHgt0)
1385 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
1388 if (!bWdt0 && !bHgt0)
1390 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
1393 if (!bLimitedRotation && !bWdt0)
1395 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
1396 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
1399 if (!bWdt0 && !bHgt0)
1401 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
1404 if (!bLimitedRotation && !bHgt0)
1406 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
1409 if (!bWdt0 && !bHgt0)
1411 maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
1416 // Diagram selection visualization support
1417 // Caution: CppunitTest_sd_tiledrendering shows that mpMarkedObj *can* actually be nullptr (!)
1418 if(nullptr != mpMarkedObj && mpMarkedObj->isDiagram())
1420 mpMarkedObj->AddToHdlList(maHdlList);
1423 const size_t nSiz1(maHdlList.GetHdlCount());
1425 // moved setting the missing parameters at SdrHdl here from the
1426 // single loop above (bSingleTextObjMark), this was missing all
1427 // the time. Setting SdrObject is now required to correctly get
1428 // the View-Dependent evtl. GridOffset adapted
1429 for (size_t i=nSiz0; i<nSiz1; ++i)
1431 SdrHdl* pHdl=maHdlList.GetHdl(i);
1432 pHdl->SetObj(mpMarkedObj);
1433 pHdl->SetPageView(mpMarkedPV);
1434 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1438 else
1440 bool bDone(false);
1442 // moved crop handling to non-frame part and the handle creation to SdrGrafObj
1443 if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
1445 // Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
1446 // behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
1447 // writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
1448 const size_t nSiz0(maHdlList.GetHdlCount());
1449 mpMarkedObj->addCropHandles(maHdlList);
1450 const size_t nSiz1(maHdlList.GetHdlCount());
1452 // Was missing: Set infos at SdrCropHdl
1453 for (size_t i=nSiz0; i<nSiz1; ++i)
1455 SdrHdl* pHdl=maHdlList.GetHdl(i);
1456 pHdl->SetObj(mpMarkedObj);
1457 pHdl->SetPageView(mpMarkedPV);
1458 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1461 bDone = true;
1464 if(!bDone)
1466 for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
1468 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1469 SdrObject* pObj=pM->GetMarkedSdrObj();
1470 SdrPageView* pPV=pM->GetPageView();
1471 const size_t nSiz0=maHdlList.GetHdlCount();
1472 pObj->AddToHdlList(maHdlList);
1473 const size_t nSiz1=maHdlList.GetHdlCount();
1474 bool bPoly=pObj->IsPolyObj();
1475 const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
1476 for (size_t i=nSiz0; i<nSiz1; ++i)
1478 SdrHdl* pHdl=maHdlList.GetHdl(i);
1479 pHdl->SetObj(pObj);
1480 pHdl->SetPageView(pPV);
1481 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1483 if (bPoly)
1485 bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
1486 pHdl->SetSelected(bSelected);
1487 if (mbPlusHdlAlways || bSelected)
1489 SdrHdlList plusList(nullptr);
1490 pObj->AddToPlusHdlList(plusList, *pHdl);
1491 sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
1492 for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
1494 SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
1495 pPlusHdl->SetObj(pObj);
1496 pPlusHdl->SetPageView(pPV);
1497 pPlusHdl->SetPlusHdl(true);
1499 plusList.MoveTo(maHdlList);
1507 // GluePoint handles
1508 for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
1510 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1511 SdrObject* pObj=pM->GetMarkedSdrObj();
1512 const SdrGluePointList* pGPL=pObj->GetGluePointList();
1513 if (!pGPL)
1514 continue;
1516 SdrPageView* pPV=pM->GetPageView();
1517 const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
1518 for (sal_uInt16 nId : rMrkGlue)
1520 //nNum changed to nNumGP because already used in for loop
1521 sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
1522 if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
1524 const SdrGluePoint& rGP=(*pGPL)[nNumGP];
1525 Point aPos(rGP.GetAbsolutePos(*pObj));
1526 std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
1527 pGlueHdl->SetObj(pObj);
1528 pGlueHdl->SetPageView(pPV);
1529 pGlueHdl->SetObjHdlNum(nId);
1530 maHdlList.AddHdl(std::move(pGlueHdl));
1535 // rotation point/axis of reflection
1536 if(!bLimitedRotation)
1538 AddDragModeHdl(meDragMode);
1541 // sort handles
1542 maHdlList.Sort();
1544 // add custom handles (used by other apps, e.g. AnchorPos)
1545 AddCustomHdl();
1547 // moved it here to access all the handles for callback.
1548 if (bTiledRendering && pViewShell)
1550 SetMarkHandlesForLOKit(aRect, pOtherShell);
1553 // try to restore focus handle index from remembered values
1554 if(!bSaveOldFocus)
1555 return;
1557 for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
1559 SdrHdl* pCandidate = maHdlList.GetHdl(a);
1561 if(pCandidate->GetObj()
1562 && pCandidate->GetObj() == pSaveObj
1563 && pCandidate->GetKind() == eSaveKind
1564 && pCandidate->GetPolyNum() == nSavePolyNum
1565 && pCandidate->GetPointNum() == nSavePointNum)
1567 maHdlList.SetFocusHdl(pCandidate);
1568 break;
1573 void SdrMarkView::AddCustomHdl()
1575 // add custom handles (used by other apps, e.g. AnchorPos)
1578 void SdrMarkView::SetDragMode(SdrDragMode eMode)
1580 SdrDragMode eMode0=meDragMode;
1581 meDragMode=eMode;
1582 if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
1583 if (meDragMode!=eMode0) {
1584 ForceRefToMarked();
1585 SetMarkHandles(nullptr);
1587 if (AreObjectsMarked()) MarkListHasChanged();
1592 void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
1594 switch(eMode)
1596 case SdrDragMode::Rotate:
1598 // add rotation center
1599 maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
1600 break;
1602 case SdrDragMode::Mirror:
1604 // add axis of reflection
1605 std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
1606 std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
1607 std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));
1609 pHdl1->SetObjHdlNum(1); // for sorting
1610 pHdl2->SetObjHdlNum(2); // for sorting
1611 pHdl3->SetObjHdlNum(3); // for sorting
1613 maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
1614 maHdlList.AddHdl(std::move(pHdl2));
1615 maHdlList.AddHdl(std::move(pHdl3));
1617 break;
1619 case SdrDragMode::Transparence:
1621 // add interactive transparency handle
1622 const size_t nMarkCount = GetMarkedObjectCount();
1623 if(nMarkCount == 1)
1625 SdrObject* pObj = GetMarkedObjectByIndex(0);
1626 SdrModel& rModel = GetModel();
1627 const SfxItemSet& rSet = pObj->GetMergedItemSet();
1629 if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
1631 // add this item, it's not yet there
1632 XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
1633 basegfx::BGradient aGrad = aNewItem.GetGradientValue();
1635 aNewItem.SetEnabled(true);
1636 aGrad.SetStartIntens(100);
1637 aGrad.SetEndIntens(100);
1638 aNewItem.SetGradientValue(aGrad);
1640 // add undo to allow user to take back this step
1641 if (rModel.IsUndoEnabled())
1643 rModel.BegUndo(SvxResId(SIP_XA_FILLTRANSPARENCE));
1644 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
1645 rModel.EndUndo();
1648 SfxItemSet aNewSet(rModel.GetItemPool());
1649 aNewSet.Put(aNewItem);
1650 pObj->SetMergedItemSetAndBroadcast(aNewSet);
1653 // set values and transform to vector set
1654 GradTransVector aGradTransVector;
1655 GradTransGradient aGradTransGradient;
1657 aGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
1658 GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
1660 // build handles
1661 const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
1662 const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
1663 std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
1664 std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
1665 std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, false));
1666 DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
1668 // link them
1669 pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
1670 pGradHdl->SetObj(pObj);
1671 pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1672 pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1674 // insert them
1675 maHdlList.AddHdl(std::move(pColHdl1));
1676 maHdlList.AddHdl(std::move(pColHdl2));
1677 maHdlList.AddHdl(std::move(pGradHdl));
1679 break;
1681 case SdrDragMode::Gradient:
1683 // add interactive gradient handle
1684 const size_t nMarkCount = GetMarkedObjectCount();
1685 if(nMarkCount == 1)
1687 SdrObject* pObj = GetMarkedObjectByIndex(0);
1688 const SfxItemSet& rSet = pObj->GetMergedItemSet();
1689 drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
1691 if(eFillStyle == drawing::FillStyle_GRADIENT)
1693 // set values and transform to vector set
1694 GradTransVector aGradTransVector;
1695 GradTransGradient aGradTransGradient;
1696 Size aHdlSize(15, 15);
1698 aGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
1699 GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
1701 // build handles
1702 const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
1703 const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
1704 std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, aHdlSize, false));
1705 std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, aHdlSize, false));
1706 std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, true));
1707 DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
1709 // link them
1710 pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
1711 pGradHdl->SetObj(pObj);
1712 pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1713 pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1715 // insert them
1716 maHdlList.AddHdl(std::move(pColHdl1));
1717 maHdlList.AddHdl(std::move(pColHdl2));
1718 maHdlList.AddHdl(std::move(pGradHdl));
1721 break;
1723 case SdrDragMode::Crop:
1725 // TODO
1726 break;
1728 default: break;
1732 /** handle mouse over effects for handles */
1733 bool SdrMarkView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
1735 if(maHdlList.GetHdlCount())
1737 SdrHdl* pMouseOverHdl = nullptr;
1738 if( !rMEvt.IsLeaveWindow() && pWin )
1740 Point aMDPos( pWin->PixelToLogic( rMEvt.GetPosPixel() ) );
1741 pMouseOverHdl = PickHandle(aMDPos);
1744 // notify last mouse over handle that he lost the mouse
1745 const size_t nHdlCount = maHdlList.GetHdlCount();
1747 for(size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
1749 SdrHdl* pCurrentHdl = GetHdl(nHdl);
1750 if( pCurrentHdl->mbMouseOver )
1752 if( pCurrentHdl != pMouseOverHdl )
1754 pCurrentHdl->mbMouseOver = false;
1755 pCurrentHdl->onMouseLeave();
1757 break;
1761 // notify current mouse over handle
1762 if( pMouseOverHdl )
1764 pMouseOverHdl->mbMouseOver = true;
1765 pMouseOverHdl->onMouseEnter(rMEvt);
1768 return SdrSnapView::MouseMove(rMEvt, pWin);
1771 bool SdrMarkView::RequestHelp(const HelpEvent& rHEvt)
1773 if (maHdlList.GetHdlCount())
1775 const size_t nHdlCount = maHdlList.GetHdlCount();
1777 for (size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
1779 SdrHdl* pCurrentHdl = GetHdl(nHdl);
1780 if (pCurrentHdl->mbMouseOver)
1782 pCurrentHdl->onHelpRequest();
1783 return true;
1787 return SdrSnapView::RequestHelp(rHEvt);
1790 void SdrMarkView::ForceRefToMarked()
1792 switch(meDragMode)
1794 case SdrDragMode::Rotate:
1796 tools::Rectangle aR(GetMarkedObjRect());
1797 maRef1 = aR.Center();
1799 break;
1802 case SdrDragMode::Mirror:
1804 // first calculate the length of the axis of reflection
1805 tools::Long nOutMin=0;
1806 tools::Long nOutMax=0;
1807 tools::Long nMinLen=0;
1808 tools::Long nObjDst=0;
1809 tools::Long nOutHgt=0;
1810 OutputDevice* pOut=GetFirstOutputDevice();
1811 if (pOut!=nullptr) {
1812 // minimum length: 50 pixels
1813 nMinLen=pOut->PixelToLogic(Size(0,50)).Height();
1814 // 20 pixels distance to the Obj for the reference point
1815 nObjDst=pOut->PixelToLogic(Size(0,20)).Height();
1816 // MinY/MaxY
1817 // margin = minimum length = 10 pixels
1818 tools::Long nDst=pOut->PixelToLogic(Size(0,10)).Height();
1819 nOutMin=-pOut->GetMapMode().GetOrigin().Y();
1820 nOutMax=pOut->GetOutputSize().Height()-1+nOutMin;
1821 nOutMin+=nDst;
1822 nOutMax-=nDst;
1823 // absolute minimum length, however, is 10 pixels
1824 if (nOutMax-nOutMin<nDst) {
1825 nOutMin+=nOutMax+1;
1826 nOutMin/=2;
1827 nOutMin-=(nDst+1)/2;
1828 nOutMax=nOutMin+nDst;
1830 nOutHgt=nOutMax-nOutMin;
1831 // otherwise minimum length = 1/4 OutHgt
1832 tools::Long nTemp=nOutHgt/4;
1833 if (nTemp>nMinLen) nMinLen=nTemp;
1836 tools::Rectangle aR(GetMarkedObjBoundRect());
1837 Point aCenter(aR.Center());
1838 tools::Long nMarkHgt=aR.GetHeight()-1;
1839 tools::Long nHgt=nMarkHgt+nObjDst*2; // 20 pixels overlapping above and below
1840 if (nHgt<nMinLen) nHgt=nMinLen; // minimum length 50 pixels or 1/4 OutHgt, respectively
1842 tools::Long nY1=aCenter.Y()-(nHgt+1)/2;
1843 tools::Long nY2=nY1+nHgt;
1845 if (pOut!=nullptr && nMinLen>nOutHgt) nMinLen=nOutHgt; // TODO: maybe shorten this a little
1847 if (pOut!=nullptr) { // now move completely into the visible area
1848 if (nY1<nOutMin) {
1849 nY1=nOutMin;
1850 if (nY2<nY1+nMinLen) nY2=nY1+nMinLen;
1852 if (nY2>nOutMax) {
1853 nY2=nOutMax;
1854 if (nY1>nY2-nMinLen) nY1=nY2-nMinLen;
1858 maRef1.setX(aCenter.X() );
1859 maRef1.setY(nY1 );
1860 maRef2.setX(aCenter.X() );
1861 maRef2.setY(nY2 );
1863 break;
1866 case SdrDragMode::Transparence:
1867 case SdrDragMode::Gradient:
1868 case SdrDragMode::Crop:
1870 tools::Rectangle aRect(GetMarkedObjBoundRect());
1871 maRef1 = aRect.TopLeft();
1872 maRef2 = aRect.BottomRight();
1873 break;
1875 default: break;
1879 void SdrMarkView::SetRef1(const Point& rPt)
1881 if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
1883 maRef1 = rPt;
1884 SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref1);
1885 if(pH)
1886 pH->SetPos(rPt);
1890 void SdrMarkView::SetRef2(const Point& rPt)
1892 if(meDragMode == SdrDragMode::Mirror)
1894 maRef2 = rPt;
1895 SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref2);
1896 if(pH)
1897 pH->SetPos(rPt);
1901 SfxViewShell* SdrMarkView::GetSfxViewShell() const
1903 return SfxViewShell::Current();
1906 void SdrMarkView::CheckMarked()
1908 for (size_t nm=GetMarkedObjectCount(); nm>0;) {
1909 --nm;
1910 SdrMark* pM = GetSdrMarkByIndex(nm);
1911 SdrObject* pObj = pM->GetMarkedSdrObj();
1912 SdrPageView* pPV = pM->GetPageView();
1913 bool bRaus = !pObj || !pPV->IsObjMarkable(pObj);
1914 if (bRaus)
1916 GetMarkedObjectListWriteAccess().DeleteMark(nm);
1918 else
1920 if (!IsGluePointEditMode()) { // selected gluepoints only in GlueEditMode
1921 SdrUShortCont& rPts = pM->GetMarkedGluePoints();
1922 rPts.clear();
1927 // at least reset the remembered BoundRect to prevent handle
1928 // generation if bForceFrameHandles is TRUE.
1929 mbMarkedObjRectDirty = true;
1932 void SdrMarkView::SetMarkRects()
1934 SdrPageView* pPV = GetSdrPageView();
1936 if(pPV)
1938 pPV->SetHasMarkedObj(GetMarkedObjectList().TakeSnapRect(pPV, pPV->MarkSnap()));
1939 GetMarkedObjectList().TakeBoundRect(pPV, pPV->MarkBound());
1943 void SdrMarkView::SetFrameHandles(bool bOn)
1945 if (bOn!=mbForceFrameHandles) {
1946 bool bOld=ImpIsFrameHandles();
1947 mbForceFrameHandles=bOn;
1948 bool bNew=ImpIsFrameHandles();
1949 if (bNew!=bOld) {
1950 AdjustMarkHdl();
1951 MarkListHasChanged();
1956 void SdrMarkView::SetEditMode(SdrViewEditMode eMode)
1958 if (eMode==meEditMode) return;
1960 bool bGlue0=meEditMode==SdrViewEditMode::GluePointEdit;
1961 bool bEdge0=static_cast<SdrCreateView*>(this)->IsEdgeTool();
1962 meEditMode0=meEditMode;
1963 meEditMode=eMode;
1964 bool bGlue1=meEditMode==SdrViewEditMode::GluePointEdit;
1965 bool bEdge1=static_cast<SdrCreateView*>(this)->IsEdgeTool();
1966 // avoid flickering when switching between GlueEdit and EdgeTool
1967 if (bGlue1 && !bGlue0) ImpSetGlueVisible2(bGlue1);
1968 if (bEdge1!=bEdge0) ImpSetGlueVisible3(bEdge1);
1969 if (!bGlue1 && bGlue0) ImpSetGlueVisible2(bGlue1);
1970 if (bGlue0 && !bGlue1) UnmarkAllGluePoints();
1974 bool SdrMarkView::IsObjMarkable(SdrObject const * pObj, SdrPageView const * pPV) const
1976 if (pObj)
1978 if (pObj->IsMarkProtect() ||
1979 (!mbDesignMode && pObj->IsUnoObj()))
1981 // object not selectable or
1982 // SdrUnoObj not in DesignMode
1983 return false;
1986 return pPV==nullptr || pPV->IsObjMarkable(pObj);
1989 bool SdrMarkView::IsMarkedObjHit(const Point& rPnt, short nTol) const
1991 bool bRet=false;
1992 nTol=ImpGetHitTolLogic(nTol,nullptr);
1993 for (size_t nm=0; nm<GetMarkedObjectCount() && !bRet; ++nm) {
1994 SdrMark* pM=GetSdrMarkByIndex(nm);
1995 bRet = nullptr != CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr);
1997 return bRet;
2000 SdrHdl* SdrMarkView::PickHandle(const Point& rPnt) const
2002 if (mbSomeObjChgdFlag) { // recalculate handles, if necessary
2003 FlushComeBackTimer();
2005 return maHdlList.IsHdlListHit(rPnt);
2008 bool SdrMarkView::MarkObj(const Point& rPnt, short nTol, bool bToggle, bool bDeep)
2010 SdrPageView* pPV;
2011 nTol=ImpGetHitTolLogic(nTol,nullptr);
2012 SdrSearchOptions nOptions=SdrSearchOptions::PICKMARKABLE;
2013 if (bDeep) nOptions=nOptions|SdrSearchOptions::DEEP;
2014 SdrObject* pObj = PickObj(rPnt, static_cast<sal_uInt16>(nTol), pPV, nOptions);
2015 if (pObj) {
2016 bool bUnmark=bToggle && IsObjMarked(pObj);
2017 MarkObj(pObj,pPV,bUnmark);
2019 return pObj != nullptr;
2022 bool SdrMarkView::MarkNextObj(bool bPrev)
2024 SdrPageView* pPageView = GetSdrPageView();
2026 if(!pPageView)
2028 return false;
2031 SortMarkedObjects();
2032 const size_t nMarkCount=GetMarkedObjectCount();
2033 size_t nChgMarkNum = SAL_MAX_SIZE; // number of the MarkEntry we want to replace
2034 size_t nSearchObjNum = bPrev ? 0 : SAL_MAX_SIZE;
2035 if (nMarkCount!=0) {
2036 nChgMarkNum=bPrev ? 0 : nMarkCount-1;
2037 SdrMark* pM=GetSdrMarkByIndex(nChgMarkNum);
2038 OSL_ASSERT(pM!=nullptr);
2039 if (pM->GetMarkedSdrObj() != nullptr)
2040 nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition();
2043 SdrObject* pMarkObj=nullptr;
2044 SdrObjList* pSearchObjList=pPageView->GetObjList();
2045 const size_t nObjCount = pSearchObjList->GetObjCount();
2046 if (nObjCount!=0) {
2047 if (nSearchObjNum>nObjCount) nSearchObjNum=nObjCount;
2048 while (pMarkObj==nullptr && ((!bPrev && nSearchObjNum>0) || (bPrev && nSearchObjNum<nObjCount)))
2050 if (!bPrev)
2051 nSearchObjNum--;
2052 SdrObject* pSearchObj = pSearchObjList->GetObjectForNavigationPosition(nSearchObjNum);
2053 if (IsObjMarkable(pSearchObj,pPageView))
2055 if (TryToFindMarkedObject(pSearchObj)==SAL_MAX_SIZE)
2057 pMarkObj=pSearchObj;
2060 if (bPrev) nSearchObjNum++;
2064 if(!pMarkObj)
2066 return false;
2069 if (nChgMarkNum!=SAL_MAX_SIZE)
2071 GetMarkedObjectListWriteAccess().DeleteMark(nChgMarkNum);
2073 MarkObj(pMarkObj,pPageView); // also calls MarkListHasChanged(), AdjustMarkHdl()
2074 return true;
2077 bool SdrMarkView::MarkNextObj(const Point& rPnt, short nTol, bool bPrev)
2079 SortMarkedObjects();
2080 nTol=ImpGetHitTolLogic(nTol,nullptr);
2081 SdrMark* pTopMarkHit=nullptr;
2082 SdrMark* pBtmMarkHit=nullptr;
2083 size_t nTopMarkHit=0;
2084 size_t nBtmMarkHit=0;
2085 // find topmost of the selected objects that is hit by rPnt
2086 const size_t nMarkCount=GetMarkedObjectCount();
2087 for (size_t nm=nMarkCount; nm>0 && pTopMarkHit==nullptr;) {
2088 --nm;
2089 SdrMark* pM=GetSdrMarkByIndex(nm);
2090 if(CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr))
2092 pTopMarkHit=pM;
2093 nTopMarkHit=nm;
2096 // nothing found, in this case, just select an object
2097 if (pTopMarkHit==nullptr) return MarkObj(rPnt,sal_uInt16(nTol));
2099 SdrObject* pTopObjHit=pTopMarkHit->GetMarkedSdrObj();
2100 SdrObjList* pObjList=pTopObjHit->getParentSdrObjListFromSdrObject();
2101 SdrPageView* pPV=pTopMarkHit->GetPageView();
2102 // find lowermost of the selected objects that is hit by rPnt
2103 // and is placed on the same PageView as pTopMarkHit
2104 for (size_t nm=0; nm<nMarkCount && pBtmMarkHit==nullptr; ++nm) {
2105 SdrMark* pM=GetSdrMarkByIndex(nm);
2106 SdrPageView* pPV2=pM->GetPageView();
2107 if (pPV2==pPV && CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pPV2,SdrSearchOptions::NONE,nullptr))
2109 pBtmMarkHit=pM;
2110 nBtmMarkHit=nm;
2113 if (pBtmMarkHit==nullptr) { pBtmMarkHit=pTopMarkHit; nBtmMarkHit=nTopMarkHit; }
2114 SdrObject* pBtmObjHit=pBtmMarkHit->GetMarkedSdrObj();
2115 const size_t nObjCount = pObjList->GetObjCount();
2117 size_t nSearchBeg(0);
2118 E3dScene* pScene(nullptr);
2119 SdrObject* pObjHit(bPrev ? pBtmObjHit : pTopObjHit);
2120 bool bRemap =
2121 nullptr != dynamic_cast< const E3dCompoundObject* >(pObjHit);
2122 if (bRemap)
2124 pScene = DynCastE3dScene(pObjHit->getParentSdrObjectFromSdrObject());
2125 bRemap = nullptr != pScene;
2128 if(bPrev)
2130 sal_uInt32 nOrdNumBtm(pBtmObjHit->GetOrdNum());
2132 if(bRemap)
2134 nOrdNumBtm = pScene->RemapOrdNum(nOrdNumBtm);
2137 nSearchBeg = nOrdNumBtm + 1;
2139 else
2141 sal_uInt32 nOrdNumTop(pTopObjHit->GetOrdNum());
2143 if(bRemap)
2145 nOrdNumTop = pScene->RemapOrdNum(nOrdNumTop);
2148 nSearchBeg = nOrdNumTop;
2151 size_t no=nSearchBeg;
2152 SdrObject* pFndObj=nullptr;
2153 while (pFndObj==nullptr && ((!bPrev && no>0) || (bPrev && no<nObjCount))) {
2154 if (!bPrev) no--;
2155 SdrObject* pObj;
2157 if(bRemap)
2159 pObj = pObjList->GetObj(pScene->RemapOrdNum(no));
2161 else
2163 pObj = pObjList->GetObj(no);
2166 if (CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr))
2168 if (TryToFindMarkedObject(pObj)==SAL_MAX_SIZE) {
2169 pFndObj=pObj;
2170 } else {
2171 // TODO: for performance reasons set on to Top or Btm, if necessary
2174 if (bPrev) no++;
2176 if (pFndObj!=nullptr)
2178 GetMarkedObjectListWriteAccess().DeleteMark(bPrev?nBtmMarkHit:nTopMarkHit);
2179 GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pFndObj,pPV));
2180 MarkListHasChanged();
2181 AdjustMarkHdl();
2183 return pFndObj!=nullptr;
2186 void SdrMarkView::MarkObj(const tools::Rectangle& rRect, bool bUnmark)
2188 bool bFnd=false;
2189 tools::Rectangle aR(rRect);
2190 SdrObjList* pObjList;
2191 BrkAction();
2192 SdrPageView* pPV = GetSdrPageView();
2194 if(pPV)
2196 pObjList=pPV->GetObjList();
2197 tools::Rectangle aFrm1(aR);
2198 for (const rtl::Reference<SdrObject>& pObj : *pObjList) {
2199 tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2200 if (aFrm1.Contains(aRect)) {
2201 if (!bUnmark) {
2202 if (IsObjMarkable(pObj.get(),pPV))
2204 GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj.get(),pPV));
2205 bFnd=true;
2207 } else {
2208 const size_t nPos=TryToFindMarkedObject(pObj.get());
2209 if (nPos!=SAL_MAX_SIZE)
2211 GetMarkedObjectListWriteAccess().DeleteMark(nPos);
2212 bFnd=true;
2218 if (bFnd) {
2219 SortMarkedObjects();
2220 MarkListHasChanged();
2221 AdjustMarkHdl();
2225 namespace {
2227 void collectUIInformation(const SdrObject* pObj)
2229 EventDescription aDescription;
2230 aDescription.aAction = "SELECT";
2231 aDescription.aParent = "MainWindow";
2232 aDescription.aKeyWord = "CurrentApp";
2234 if (!pObj->GetName().isEmpty())
2235 aDescription.aParameters = {{"OBJECT", pObj->GetName()}};
2236 else
2237 aDescription.aParameters = {{"OBJECT", "Unnamed_Obj_" + OUString::number(pObj->GetOrdNum())}};
2239 UITestLogger::getInstance().logEvent(aDescription);
2244 void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bDoNoSetMarkHdl,
2245 std::vector<basegfx::B2DRectangle> && rSubSelections)
2247 if (!(pObj!=nullptr && pPV!=nullptr && IsObjMarkable(pObj, pPV)))
2248 return;
2250 BrkAction();
2251 if (!bUnmark)
2253 GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
2254 collectUIInformation(pObj);
2256 else
2258 const size_t nPos=TryToFindMarkedObject(pObj);
2259 if (nPos!=SAL_MAX_SIZE)
2261 GetMarkedObjectListWriteAccess().DeleteMark(nPos);
2265 maSubSelectionList = std::move(rSubSelections);
2267 if (!bDoNoSetMarkHdl) {
2268 MarkListHasChanged();
2269 AdjustMarkHdl();
2273 bool SdrMarkView::IsObjMarked(SdrObject const * pObj) const
2275 return TryToFindMarkedObject(pObj)!=SAL_MAX_SIZE;
2278 sal_uInt16 SdrMarkView::GetMarkHdlSizePixel() const
2280 return maHdlList.GetHdlSize()*2+1;
2283 void SdrMarkView::SetMarkHdlSizePixel(sal_uInt16 nSiz)
2285 if (nSiz<3) nSiz=3;
2286 nSiz/=2;
2287 if (nSiz!=maHdlList.GetHdlSize()) {
2288 maHdlList.SetHdlSize(nSiz);
2292 bool SdrMarkView::getPossibleGridOffsetForSdrObject(
2293 basegfx::B2DVector& rOffset,
2294 const SdrObject* pObj,
2295 const SdrPageView* pPV) const
2297 if(nullptr == pObj || nullptr == pPV)
2299 return false;
2302 const OutputDevice* pOutputDevice(GetFirstOutputDevice());
2304 if(nullptr == pOutputDevice)
2306 return false;
2309 const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
2311 if(nullptr == pSdrPageWindow)
2313 return false;
2316 const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
2318 if(!rObjectContact.supportsGridOffsets())
2320 return false;
2323 const sdr::contact::ViewObjectContact& rVOC(pObj->GetViewContact().GetViewObjectContact(
2324 const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
2326 rOffset = rVOC.getGridOffset();
2328 return !rOffset.equalZero();
2331 bool SdrMarkView::getPossibleGridOffsetForPosition(
2332 basegfx::B2DVector& rOffset,
2333 const basegfx::B2DPoint& rPoint,
2334 const SdrPageView* pPV) const
2336 if(nullptr == pPV)
2338 return false;
2341 const OutputDevice* pOutputDevice(GetFirstOutputDevice());
2343 if(nullptr == pOutputDevice)
2345 return false;
2348 const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
2350 if(nullptr == pSdrPageWindow)
2352 return false;
2355 const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
2357 if(!rObjectContact.supportsGridOffsets())
2359 return false;
2362 rObjectContact.calculateGridOffsetForB2DRange(rOffset, basegfx::B2DRange(rPoint));
2364 return !rOffset.equalZero();
2367 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const
2369 if(((nOptions & SdrSearchOptions::IMPISMASTER) && pObj->IsNotVisibleAsMaster()) || (!pObj->IsVisible()))
2371 return nullptr;
2374 const bool bCheckIfMarkable(nOptions & SdrSearchOptions::TESTMARKABLE);
2375 const bool bDeep(nOptions & SdrSearchOptions::DEEP);
2376 const bool bOLE(dynamic_cast< const SdrOle2Obj* >(pObj) != nullptr);
2377 auto pTextObj = DynCastSdrTextObj( pObj);
2378 const bool bTXT(pTextObj && pTextObj->IsTextFrame());
2379 SdrObject* pRet=nullptr;
2380 tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2382 // add possible GridOffset to up-to-now view-independent BoundRect data
2383 basegfx::B2DVector aGridOffset(0.0, 0.0);
2384 if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, pPV))
2386 aRect += Point(
2387 basegfx::fround(aGridOffset.getX()),
2388 basegfx::fround(aGridOffset.getY()));
2391 double nTol2(nTol);
2393 // double tolerance for OLE, text frames and objects in
2394 // active text edit
2395 if(bOLE || bTXT || pObj==static_cast<const SdrObjEditView*>(this)->GetTextEditObject())
2397 nTol2*=2;
2400 aRect.AdjustLeft( -nTol2 ); // add 1 tolerance for all objects
2401 aRect.AdjustTop( -nTol2 );
2402 aRect.AdjustRight(nTol2 );
2403 aRect.AdjustBottom(nTol2 );
2405 if (aRect.Contains(rPnt))
2407 if (!bCheckIfMarkable || IsObjMarkable(pObj,pPV))
2409 SdrObjList* pOL=pObj->GetSubList();
2411 if (pOL!=nullptr && pOL->GetObjCount()!=0)
2413 SdrObject* pTmpObj;
2414 // adjustment hit point for virtual objects
2415 Point aPnt( rPnt );
2417 if ( auto pVirtObj = dynamic_cast<const SdrVirtObj*>( pObj) )
2419 Point aOffset = pVirtObj->GetOffset();
2420 aPnt.Move( -aOffset.X(), -aOffset.Y() );
2423 pRet=CheckSingleSdrObjectHit(aPnt,nTol,pOL,pPV,nOptions,pMVisLay,pTmpObj);
2425 else
2427 if(!pMVisLay || pMVisLay->IsSet(pObj->GetLayer()))
2429 pRet = SdrObjectPrimitiveHit(*pObj, rPnt, {nTol2, nTol2}, *pPV, &pPV->GetVisibleLayers(), false);
2435 if (!bDeep && pRet!=nullptr)
2437 pRet=pObj;
2440 return pRet;
2443 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj) const
2445 return (*this).CheckSingleSdrObjectHit(rPnt,nTol,pOL,pPV,nOptions,pMVisLay,rpRootObj,nullptr);
2447 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
2449 SdrObject* pRet=nullptr;
2450 rpRootObj=nullptr;
2451 if (!pOL)
2452 return nullptr;
2453 const E3dScene* pRemapScene = DynCastE3dScene(pOL->getSdrObjectFromSdrObjList());
2454 const size_t nObjCount(pOL->GetObjCount());
2455 size_t nObjNum(nObjCount);
2457 while (pRet==nullptr && nObjNum>0)
2459 nObjNum--;
2460 SdrObject* pObj;
2462 if(pRemapScene)
2464 pObj = pOL->GetObj(pRemapScene->RemapOrdNum(nObjNum));
2466 else
2468 pObj = pOL->GetObj(nObjNum);
2470 if (nOptions & SdrSearchOptions::BEFOREMARK)
2472 if (pMarkList!=nullptr)
2474 if ((*pMarkList).FindObject(pObj)!=SAL_MAX_SIZE)
2476 return nullptr;
2480 pRet=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,pMVisLay);
2481 if (pRet!=nullptr) rpRootObj=pObj;
2483 return pRet;
2486 SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
2488 return PickObj(rPnt, nTol, rpPV, nOptions, nullptr);
2491 SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions, SdrObject** ppRootObj, bool* pbHitPassDirect) const
2492 { // TODO: lacks a Pass2,Pass3
2493 SortMarkedObjects();
2494 if (ppRootObj!=nullptr) *ppRootObj=nullptr;
2495 if (pbHitPassDirect!=nullptr) *pbHitPassDirect=true;
2496 SdrObject* pRet = nullptr;
2497 rpPV=nullptr;
2498 bool bMarked(nOptions & SdrSearchOptions::MARKED);
2499 bool bMasters=!bMarked && bool(nOptions & SdrSearchOptions::ALSOONMASTER);
2500 // nOptions & SdrSearchOptions::NEXT: n.i.
2501 // nOptions & SdrSearchOptions::PASS2BOUND: n.i.
2502 // nOptions & SdrSearchOptions::PASS3NEAREST// n.i.
2503 if (nTol<0) nTol=ImpGetHitTolLogic(nTol,nullptr);
2504 SdrObject* pObj=nullptr;
2505 SdrObject* pHitObj=nullptr;
2506 SdrPageView* pPV=nullptr;
2507 if (static_cast<const SdrObjEditView*>(this)->IsTextEditFrameHit(rPnt)) {
2508 pObj=static_cast<const SdrObjEditView*>(this)->GetTextEditObject();
2509 pHitObj=pObj;
2510 pPV=static_cast<const SdrObjEditView*>(this)->GetTextEditPageView();
2512 if (bMarked) {
2513 const size_t nMrkCnt=GetMarkedObjectCount();
2514 size_t nMrkNum=nMrkCnt;
2515 while (pHitObj==nullptr && nMrkNum>0) {
2516 nMrkNum--;
2517 SdrMark* pM=GetSdrMarkByIndex(nMrkNum);
2518 pObj=pM->GetMarkedSdrObj();
2519 pPV=pM->GetPageView();
2520 pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,nullptr);
2523 else
2525 pPV = GetSdrPageView();
2527 if(pPV)
2529 SdrPage* pPage=pPV->GetPage();
2530 sal_uInt16 nPgCount=1;
2532 if(bMasters && pPage->TRG_HasMasterPage())
2534 nPgCount++;
2536 bool bWholePage(nOptions & SdrSearchOptions::WHOLEPAGE);
2537 bool bExtraPassForWholePage=bWholePage && pPage!=pPV->GetObjList();
2538 if (bExtraPassForWholePage) nPgCount++; // First search in AktObjList, then on the entire page
2539 sal_uInt16 nPgNum=nPgCount;
2540 while (pHitObj==nullptr && nPgNum>0) {
2541 SdrSearchOptions nTmpOptions=nOptions;
2542 nPgNum--;
2543 const SdrLayerIDSet* pMVisLay=nullptr;
2544 SdrObjList* pObjList=nullptr;
2545 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = true;
2546 if (nPgNum>=nPgCount-1 || (bExtraPassForWholePage && nPgNum>=nPgCount-2))
2548 pObjList=pPV->GetObjList();
2549 if (bExtraPassForWholePage && nPgNum==nPgCount-2) {
2550 pObjList=pPage;
2551 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
2554 else
2556 // otherwise MasterPage
2557 SdrPage& rMasterPage = pPage->TRG_GetMasterPage();
2558 pMVisLay = &pPage->TRG_GetMasterPageVisibleLayers();
2559 pObjList = &rMasterPage;
2561 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
2562 nTmpOptions=nTmpOptions | SdrSearchOptions::IMPISMASTER;
2564 pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObjList,pPV,nTmpOptions,pMVisLay,pObj,&(GetMarkedObjectList()));
2568 if (pHitObj!=nullptr) {
2569 if (ppRootObj!=nullptr) *ppRootObj=pObj;
2570 if (nOptions & SdrSearchOptions::DEEP) pObj=pHitObj;
2571 if (nOptions & SdrSearchOptions::TESTTEXTEDIT) {
2572 if (!pObj->HasTextEdit() || pPV->GetLockedLayers().IsSet(pObj->GetLayer())) {
2573 pObj=nullptr;
2576 if (pObj!=nullptr && (nOptions & SdrSearchOptions::TESTMACRO)) {
2577 SdrObjMacroHitRec aHitRec;
2578 aHitRec.aPos=rPnt;
2579 aHitRec.nTol=nTol;
2580 aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
2581 aHitRec.pPageView=pPV;
2582 if (!pObj->HasMacro() || !pObj->IsMacroHit(aHitRec)) pObj=nullptr;
2584 if (pObj!=nullptr) {
2585 pRet=pObj;
2586 rpPV=pPV;
2589 return pRet;
2592 bool SdrMarkView::PickMarkedObj(const Point& rPnt, SdrObject*& rpObj, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
2594 SortMarkedObjects();
2595 const bool bBoundCheckOn2ndPass(nOptions & SdrSearchOptions::PASS2BOUND);
2596 rpObj=nullptr;
2597 rpPV=nullptr;
2598 const size_t nMarkCount=GetMarkedObjectCount();
2599 for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
2600 --nMarkNum;
2601 SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
2602 SdrPageView* pPV=pM->GetPageView();
2603 SdrObject* pObj=pM->GetMarkedSdrObj();
2604 if (CheckSingleSdrObjectHit(rPnt,mnHitTolLog,pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr)) {
2605 rpObj=pObj;
2606 rpPV=pPV;
2607 return true;
2610 if (bBoundCheckOn2ndPass) {
2611 for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
2612 --nMarkNum;
2613 SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
2614 SdrPageView* pPV=pM->GetPageView();
2615 SdrObject* pObj=pM->GetMarkedSdrObj();
2616 tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2617 aRect.AdjustLeft( -mnHitTolLog );
2618 aRect.AdjustTop( -mnHitTolLog );
2619 aRect.AdjustRight(mnHitTolLog );
2620 aRect.AdjustBottom(mnHitTolLog );
2621 if (aRect.Contains(rPnt)) {
2622 rpObj=pObj;
2623 rpPV=pPV;
2624 return true;
2628 return false;
2632 void SdrMarkView::UnmarkAllObj(SdrPageView const * pPV)
2634 if (GetMarkedObjectCount()==0)
2635 return;
2637 BrkAction();
2638 if (pPV!=nullptr)
2640 GetMarkedObjectListWriteAccess().DeletePageView(*pPV);
2642 else
2644 GetMarkedObjectListWriteAccess().Clear();
2646 mpMarkedObj=nullptr;
2647 mpMarkedPV=nullptr;
2648 MarkListHasChanged();
2649 AdjustMarkHdl();
2652 void SdrMarkView::MarkAllObj(SdrPageView* pPV)
2654 BrkAction();
2656 if(!pPV)
2658 pPV = GetSdrPageView();
2661 // #i69171# pPV may still be NULL if there is no SDrPageView (!), e.g. when inserting
2662 // other files
2663 if(pPV)
2665 const bool bMarkChg(GetMarkedObjectListWriteAccess().InsertPageView(*pPV));
2667 if(bMarkChg)
2669 MarkListHasChanged();
2673 if(GetMarkedObjectCount())
2675 AdjustMarkHdl();
2679 void SdrMarkView::AdjustMarkHdl(SfxViewShell* pOtherShell)
2681 CheckMarked();
2682 SetMarkRects();
2683 SetMarkHandles(pOtherShell);
2686 // BoundRect in model coordinates, no GridOffset added
2687 tools::Rectangle SdrMarkView::GetMarkedObjBoundRect() const
2689 tools::Rectangle aRect;
2690 for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
2691 SdrMark* pM=GetSdrMarkByIndex(nm);
2692 SdrObject* pO=pM->GetMarkedSdrObj();
2693 tools::Rectangle aR1(pO->GetCurrentBoundRect());
2694 if (aRect.IsEmpty()) aRect=aR1;
2695 else aRect.Union(aR1);
2697 return aRect;
2700 // ObjRect in model coordinates, no GridOffset added
2701 const tools::Rectangle& SdrMarkView::GetMarkedObjRect() const
2703 if (mbMarkedObjRectDirty) {
2704 const_cast<SdrMarkView*>(this)->mbMarkedObjRectDirty=false;
2705 tools::Rectangle aRect;
2706 for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
2707 SdrMark* pM=GetSdrMarkByIndex(nm);
2708 SdrObject* pO = pM->GetMarkedSdrObj();
2709 if (!pO)
2710 continue;
2711 tools::Rectangle aR1(pO->GetSnapRect());
2712 if (aRect.IsEmpty()) aRect=aR1;
2713 else aRect.Union(aR1);
2715 const_cast<SdrMarkView*>(this)->maMarkedObjRect=aRect;
2717 return maMarkedObjRect;
2721 OUString SdrMarkView::ImpGetDescriptionString(TranslateId pStrCacheID, ImpGetDescriptionOptions nOpt) const
2723 OUString sStr = SvxResId(pStrCacheID);
2724 const sal_Int32 nPos = sStr.indexOf("%1");
2726 if(nPos != -1)
2728 if(nOpt == ImpGetDescriptionOptions::POINTS)
2730 sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedPoints());
2732 else if(nOpt == ImpGetDescriptionOptions::GLUEPOINTS)
2734 sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedGluePoints());
2736 else
2738 sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedObjects());
2742 return sStr.replaceFirst("%2", "0");
2746 void SdrMarkView::EnterMarkedGroup()
2748 // We enter only the first group found (in only one PageView), because
2749 // PageView::EnterGroup calls an AdjustMarkHdl.
2750 // TODO: I'll have to prevent that via a flag.
2751 SdrPageView* pPV = GetSdrPageView();
2753 if(!pPV)
2754 return;
2756 bool bEnter=false;
2757 for (size_t nm = GetMarkedObjectCount(); nm > 0 && !bEnter;)
2759 --nm;
2760 SdrMark* pM=GetSdrMarkByIndex(nm);
2761 if (pM->GetPageView()==pPV) {
2762 SdrObject* pObj=pM->GetMarkedSdrObj();
2763 if (pObj->IsGroupObject()) {
2764 if (pPV->EnterGroup(pObj)) {
2765 bEnter=true;
2773 void SdrMarkView::MarkListHasChanged()
2775 GetMarkedObjectListWriteAccess().SetNameDirty();
2776 maSdrViewSelection.SetEdgesOfMarkedNodesDirty();
2778 mbMarkedObjRectDirty=true;
2779 mbMarkedPointsRectsDirty=true;
2780 bool bOneEdgeMarked=false;
2781 if (GetMarkedObjectCount()==1) {
2782 const SdrObject* pObj=GetMarkedObjectByIndex(0);
2783 if (pObj->GetObjInventor()==SdrInventor::Default) {
2784 bOneEdgeMarked = pObj->GetObjIdentifier() == SdrObjKind::Edge;
2787 ImpSetGlueVisible4(bOneEdgeMarked);
2791 void SdrMarkView::SetMoveOutside(bool bOn)
2793 maHdlList.SetMoveOutside(bOn);
2796 void SdrMarkView::SetDesignMode( bool bOn )
2798 if ( mbDesignMode != bOn )
2800 mbDesignMode = bOn;
2801 SdrPageView* pPageView = GetSdrPageView();
2802 if ( pPageView )
2803 pPageView->SetDesignMode( bOn );
2807 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */