1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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 
27 #include <osl/diagnose.h>
28 #include <osl/thread.h>
29 #include <rtl/strbuf.hxx>
30 #include <svx/svdoole2.hxx>
31 #include <svx/xgrad.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 
55 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
56 #include <comphelper/lok.hxx>
57 #include <sfx2/lokhelper.hxx>
58 #include <sfx2/lokcharthelper.hxx>
59 #include <sfx2/viewsh.hxx>
60 
61 #include <array>
62 
63 #include <com/sun/star/frame/XController.hpp>
64 #include <com/sun/star/view/XSelectionSupplier.hpp>
65 
66 #include <boost/property_tree/json_parser.hpp>
67 #include <boost/optional/optional.hpp>
68 #include <tools/UnitConversion.hxx>
69 
70 using namespace com::sun::star;
71 
72 // Migrate Marking of Objects, Points and GluePoints
73 
74 class ImplMarkingOverlay
75 {
76     // The OverlayObjects
77     sdr::overlay::OverlayObjectList               maObjects;
78 
79     // The remembered second position in logical coordinates
80     basegfx::B2DPoint                               maSecondPosition;
81 
82     // A flag to remember if the action is for unmarking.
83     bool                                            mbUnmarking : 1;
84 
85 public:
86     ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking);
87 
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.
91 
92     void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
IsUnmarking() const93     bool IsUnmarking() const { return mbUnmarking; }
94 };
95 
ImplMarkingOverlay(const SdrPaintView & rView,const basegfx::B2DPoint & rStartPos,bool bUnmarking)96 ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
97 :   maSecondPosition(rStartPos),
98     mbUnmarking(bUnmarking)
99 {
100     if (comphelper::LibreOfficeKit::isActive())
101         return; // We do client-side object manipulation with the Kit API
102 
103     for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
104     {
105         SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
106         const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
107 
108         if (xTargetOverlay.is())
109         {
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));
114         }
115     }
116 }
117 
SetSecondPosition(const basegfx::B2DPoint & rNewPosition)118 void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
119 {
120     if(rNewPosition != maSecondPosition)
121     {
122         // apply to OverlayObjects
123         for(sal_uInt32 a(0); a < maObjects.count(); a++)
124         {
125             sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
126             rCandidate.setSecondPosition(rNewPosition);
127         }
128 
129         // remember new position
130         maSecondPosition = rNewPosition;
131     }
132 }
133 
134 class MarkingSubSelectionOverlay
135 {
136     sdr::overlay::OverlayObjectList maObjects;
137 
138 public:
MarkingSubSelectionOverlay(const SdrPaintView & rView,std::vector<basegfx::B2DRectangle> const & rSelections)139     MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
140     {
141         if (comphelper::LibreOfficeKit::isActive())
142             return; // We do client-side object manipulation with the Kit API
143 
144         for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
145         {
146             SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
147             const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
148 
149             if (xTargetOverlay.is())
150             {
151                 const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
152                 const Color aHighlightColor = aSvtOptionsDrawinglayer.getHilightColor();
153 
154                 std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
155                     std::make_unique<sdr::overlay::OverlaySelection>(
156                         sdr::overlay::OverlayType::Transparent,
157                         aHighlightColor, rSelections, false);
158 
159                 xTargetOverlay->add(*pNew);
160                 maObjects.append(std::move(pNew));
161             }
162         }
163     }
164 };
165 
SdrMarkView(SdrModel & rSdrModel,OutputDevice * pOut)166 SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
167     : SdrSnapView(rSdrModel, pOut)
168     , mpMarkedObj(nullptr)
169     , mpMarkedPV(nullptr)
170     , maHdlList(this)
171     , meDragMode(SdrDragMode::Move)
172     , meEditMode(SdrViewEditMode::Edit)
173     , meEditMode0(SdrViewEditMode::Edit)
174     , mbDesignMode(false)
175     , mbForceFrameHandles(false)
176     , mbPlusHdlAlways(false)
177     , mbInsPolyPoint(false)
178     , mbMarkedObjRectDirty(false)
179     , mbMrkPntDirty(false)
180     , mbMarkedPointsRectsDirty(false)
181     , mbMarkHandlesHidden(false)
182 {
183 
184     BrkMarkObj();
185     BrkMarkPoints();
186     BrkMarkGluePoints();
187 
188     StartListening(rSdrModel);
189 }
190 
~SdrMarkView()191 SdrMarkView::~SdrMarkView()
192 {
193     // Migrate selections
194     BrkMarkObj();
195     BrkMarkPoints();
196     BrkMarkGluePoints();
197 }
198 
Notify(SfxBroadcaster & rBC,const SfxHint & rHint)199 void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
200 {
201     if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
202     {
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)
206         {
207             mbMarkedObjRectDirty=true;
208             mbMarkedPointsRectsDirty=true;
209         }
210     }
211     SdrSnapView::Notify(rBC,rHint);
212 }
213 
ModelHasChanged()214 void SdrMarkView::ModelHasChanged()
215 {
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();
230     }
231 
232     if (!(comphelper::LibreOfficeKit::isActive() && GetMarkedObjectCount() > 0))
233         return;
234 
235     //TODO: Is MarkedObjRect valid at this point?
236     tools::Rectangle aSelection(GetMarkedObjRect());
237     OString sSelection;
238     if (aSelection.IsEmpty())
239         sSelection = "EMPTY";
240     else
241     {
242         sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
243         if (nTotalPaintWindows == 1)
244         {
245             const OutputDevice* pOut = this->GetFirstOutputDevice();
246             const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
247             if (pWin && pWin->IsChart())
248             {
249                 const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
250                 if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
251                 {
252                     Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
253                     Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
254                     aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
255                 }
256             }
257         }
258 
259         // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
260         if (mpMarkedPV)
261         {
262             if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
263             {
264                 if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
265                     aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
266             }
267         }
268 
269         sSelection = aSelection.toString();
270     }
271 
272     if(SfxViewShell* pViewShell = GetSfxViewShell())
273         SfxLokHelper::notifyInvalidation(pViewShell, sSelection);
274 }
275 
276 
IsAction() const277 bool SdrMarkView::IsAction() const
278 {
279     return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
280 }
281 
MovAction(const Point & rPnt)282 void SdrMarkView::MovAction(const Point& rPnt)
283 {
284     SdrSnapView::MovAction(rPnt);
285 
286     if(IsMarkObj())
287     {
288         MovMarkObj(rPnt);
289     }
290     else if(IsMarkPoints())
291     {
292         MovMarkPoints(rPnt);
293     }
294     else if(IsMarkGluePoints())
295     {
296         MovMarkGluePoints(rPnt);
297     }
298 }
299 
EndAction()300 void SdrMarkView::EndAction()
301 {
302     if(IsMarkObj())
303     {
304         EndMarkObj();
305     }
306     else if(IsMarkPoints())
307     {
308         EndMarkPoints();
309     }
310     else if(IsMarkGluePoints())
311     {
312         EndMarkGluePoints();
313     }
314 
315     SdrSnapView::EndAction();
316 }
317 
BckAction()318 void SdrMarkView::BckAction()
319 {
320     SdrSnapView::BckAction();
321     BrkMarkObj();
322     BrkMarkPoints();
323     BrkMarkGluePoints();
324 }
325 
BrkAction()326 void SdrMarkView::BrkAction()
327 {
328     SdrSnapView::BrkAction();
329     BrkMarkObj();
330     BrkMarkPoints();
331     BrkMarkGluePoints();
332 }
333 
TakeActionRect(tools::Rectangle & rRect) const334 void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
335 {
336     if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
337     {
338         rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
339     }
340     else
341     {
342         SdrSnapView::TakeActionRect(rRect);
343     }
344 }
345 
346 
ClearPageView()347 void SdrMarkView::ClearPageView()
348 {
349     UnmarkAllObj();
350     SdrSnapView::ClearPageView();
351 }
352 
HideSdrPage()353 void SdrMarkView::HideSdrPage()
354 {
355     bool bMrkChg(false);
356 
357     SdrPageView* pPageView = GetSdrPageView();
358     if (pPageView)
359     {
360         // break all creation actions when hiding page (#75081#)
361         BrkAction();
362 
363         // Discard all selections on this page
364         bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
365     }
366 
367     SdrSnapView::HideSdrPage();
368 
369     if(bMrkChg)
370     {
371         MarkListHasChanged();
372         AdjustMarkHdl();
373     }
374 }
375 
376 
BegMarkObj(const Point & rPnt,bool bUnmark)377 void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
378 {
379     BrkAction();
380 
381     DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");
382 
383     basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
384     mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
385 
386     maDragStat.Reset(rPnt);
387     maDragStat.NextPoint();
388     maDragStat.SetMinMove(mnMinMovLog);
389 }
390 
MovMarkObj(const Point & rPnt)391 void SdrMarkView::MovMarkObj(const Point& rPnt)
392 {
393     if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
394     {
395         maDragStat.NextMove(rPnt);
396         DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
397         basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
398         mpMarkObjOverlay->SetSecondPosition(aNewPos);
399     }
400 }
401 
EndMarkObj()402 bool SdrMarkView::EndMarkObj()
403 {
404     bool bRetval(false);
405 
406     if(IsMarkObj())
407     {
408         if(maDragStat.IsMinMoved())
409         {
410             tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
411             aRect.Justify();
412             MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
413             bRetval = true;
414         }
415 
416         // cleanup
417         BrkMarkObj();
418     }
419 
420     return bRetval;
421 }
422 
BrkMarkObj()423 void SdrMarkView::BrkMarkObj()
424 {
425     if(IsMarkObj())
426     {
427         DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
428         mpMarkObjOverlay.reset();
429     }
430 }
431 
432 
BegMarkPoints(const Point & rPnt,bool bUnmark)433 bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
434 {
435     if(HasMarkablePoints())
436     {
437         BrkAction();
438 
439         DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
440         basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
441         mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
442 
443         maDragStat.Reset(rPnt);
444         maDragStat.NextPoint();
445         maDragStat.SetMinMove(mnMinMovLog);
446 
447         return true;
448     }
449 
450     return false;
451 }
452 
MovMarkPoints(const Point & rPnt)453 void SdrMarkView::MovMarkPoints(const Point& rPnt)
454 {
455     if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
456     {
457         maDragStat.NextMove(rPnt);
458 
459         DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
460         basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
461         mpMarkPointsOverlay->SetSecondPosition(aNewPos);
462     }
463 }
464 
EndMarkPoints()465 bool SdrMarkView::EndMarkPoints()
466 {
467     bool bRetval(false);
468 
469     if(IsMarkPoints())
470     {
471         if(maDragStat.IsMinMoved())
472         {
473             tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
474             aRect.Justify();
475             MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());
476 
477             bRetval = true;
478         }
479 
480         // cleanup
481         BrkMarkPoints();
482     }
483 
484     return bRetval;
485 }
486 
BrkMarkPoints()487 void SdrMarkView::BrkMarkPoints()
488 {
489     if(IsMarkPoints())
490     {
491         DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
492         mpMarkPointsOverlay.reset();
493     }
494 }
495 
496 
BegMarkGluePoints(const Point & rPnt,bool bUnmark)497 bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
498 {
499     if(HasMarkableGluePoints())
500     {
501         BrkAction();
502 
503         DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");
504 
505         basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
506         mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
507         maDragStat.Reset(rPnt);
508         maDragStat.NextPoint();
509         maDragStat.SetMinMove(mnMinMovLog);
510 
511         return true;
512     }
513 
514     return false;
515 }
516 
MovMarkGluePoints(const Point & rPnt)517 void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
518 {
519     if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
520     {
521         maDragStat.NextMove(rPnt);
522 
523         DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
524         basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
525         mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
526     }
527 }
528 
EndMarkGluePoints()529 void SdrMarkView::EndMarkGluePoints()
530 {
531     if(IsMarkGluePoints())
532     {
533         if(maDragStat.IsMinMoved())
534         {
535             tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
536             aRect.Justify();
537             MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
538         }
539 
540         // cleanup
541         BrkMarkGluePoints();
542     }
543 }
544 
BrkMarkGluePoints()545 void SdrMarkView::BrkMarkGluePoints()
546 {
547     if(IsMarkGluePoints())
548     {
549         DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
550         mpMarkGluePointsOverlay.reset();
551     }
552 }
553 
MarkableObjectsExceed(int n) const554 bool SdrMarkView::MarkableObjectsExceed( int n ) const
555 {
556     SdrPageView* pPV = GetSdrPageView();
557     if (!pPV)
558         return false;
559 
560     SdrObjList* pOL=pPV->GetObjList();
561     const size_t nObjCount = pOL->GetObjCount();
562     for (size_t nObjNum=0; nObjNum<nObjCount; ++nObjNum) {
563         SdrObject* pObj=pOL->GetObj(nObjNum);
564         if (IsObjMarkable(pObj,pPV) && --n<0)
565             return true;
566     }
567 
568     return false;
569 }
570 
hideMarkHandles()571 void SdrMarkView::hideMarkHandles()
572 {
573     if(!mbMarkHandlesHidden)
574     {
575         mbMarkHandlesHidden = true;
576         AdjustMarkHdl();
577     }
578 }
579 
showMarkHandles()580 void SdrMarkView::showMarkHandles()
581 {
582     if(mbMarkHandlesHidden)
583     {
584         mbMarkHandlesHidden = false;
585         AdjustMarkHdl();
586     }
587 }
588 
ImpIsFrameHandles() const589 bool SdrMarkView::ImpIsFrameHandles() const
590 {
591     const size_t nMarkCount=GetMarkedObjectCount();
592     bool bFrmHdl=nMarkCount>static_cast<size_t>(mnFrameHandlesLimit) || mbForceFrameHandles;
593     bool bStdDrag=meDragMode==SdrDragMode::Move;
594     if (nMarkCount==1 && bStdDrag && bFrmHdl)
595     {
596         const SdrObject* pObj=GetMarkedObjectByIndex(0);
597         if (pObj->GetObjInventor()==SdrInventor::Default)
598         {
599             sal_uInt16 nIdent=pObj->GetObjIdentifier();
600             if (nIdent==OBJ_LINE || nIdent==OBJ_EDGE || nIdent==OBJ_CAPTION || nIdent==OBJ_MEASURE || nIdent==OBJ_CUSTOMSHAPE || nIdent==OBJ_TABLE )
601             {
602                 bFrmHdl=false;
603             }
604         }
605     }
606     if (!bStdDrag && !bFrmHdl) {
607         // all other drag modes only with FrameHandles
608         bFrmHdl=true;
609         if (meDragMode==SdrDragMode::Rotate) {
610             // when rotating, use ObjOwn drag, if there's at least 1 PolyObj
611             for (size_t nMarkNum=0; nMarkNum<nMarkCount && bFrmHdl; ++nMarkNum) {
612                 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
613                 const SdrObject* pObj=pM->GetMarkedSdrObj();
614                 bFrmHdl=!pObj->IsPolyObj();
615             }
616         }
617     }
618     if (!bFrmHdl) {
619         // FrameHandles, if at least 1 Obj can't do SpecialDrag
620         for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bFrmHdl; ++nMarkNum) {
621             const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
622             const SdrObject* pObj=pM->GetMarkedSdrObj();
623             bFrmHdl=!pObj->hasSpecialDrag();
624         }
625     }
626 
627     // no FrameHdl for crop
628     if(bFrmHdl && SdrDragMode::Crop == meDragMode)
629     {
630         bFrmHdl = false;
631     }
632 
633     return bFrmHdl;
634 }
635 
636 namespace
637 {
lcl_getDragMethodServiceName(const OUString & rCID)638 OUString lcl_getDragMethodServiceName( const OUString& rCID )
639 {
640     OUString aRet;
641 
642     sal_Int32 nIndexStart = rCID.indexOf( "DragMethod=" );
643     if( nIndexStart != -1 )
644     {
645         nIndexStart = rCID.indexOf( '=', nIndexStart );
646         if( nIndexStart != -1 )
647         {
648             nIndexStart++;
649             sal_Int32 nNextSlash = rCID.indexOf( '/', nIndexStart );
650             if( nNextSlash != -1 )
651             {
652                 sal_Int32 nIndexEnd = nNextSlash;
653                 sal_Int32 nNextColon = rCID.indexOf( ':', nIndexStart );
654                 if( nNextColon < nNextSlash )
655                     nIndexEnd = nNextColon;
656                 aRet = rCID.copy(nIndexStart,nIndexEnd-nIndexStart);
657             }
658         }
659     }
660     return aRet;
661 }
662 
lcl_getDragParameterString(const OUString & rCID)663 OUString lcl_getDragParameterString( const OUString& rCID )
664 {
665     OUString aRet;
666 
667     sal_Int32 nIndexStart = rCID.indexOf( "DragParameter=" );
668     if( nIndexStart != -1 )
669     {
670         nIndexStart = rCID.indexOf( '=', nIndexStart );
671         if( nIndexStart != -1 )
672         {
673             nIndexStart++;
674             sal_Int32 nNextSlash = rCID.indexOf( '/', nIndexStart );
675             if( nNextSlash != -1 )
676             {
677                 sal_Int32 nIndexEnd = nNextSlash;
678                 sal_Int32 nNextColon = rCID.indexOf( ':', nIndexStart );
679                 if( nNextColon < nNextSlash )
680                     nIndexEnd = nNextColon;
681                 aRet = rCID.copy(nIndexStart,nIndexEnd-nIndexStart);
682             }
683         }
684     }
685     return aRet;
686 }
687 } // anonymous namespace
688 
dumpGluePointsToJSON(boost::property_tree::ptree & rTree)689 bool SdrMarkView::dumpGluePointsToJSON(boost::property_tree::ptree& rTree)
690 {
691     bool result = false;
692     if (OutputDevice* pOutDev = mpMarkedPV ? mpMarkedPV->GetView().GetFirstOutputDevice() : nullptr)
693     {
694         bool bConvertUnit = false;
695         if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
696             bConvertUnit = true;
697         const SdrObjList* pOL = mpMarkedPV->GetObjList();
698         if (!pOL)
699             return false;
700         const size_t nObjCount = pOL->GetObjCount();
701         boost::property_tree::ptree elements;
702         for (size_t nObjNum = 0; nObjNum < nObjCount; ++nObjNum)
703         {
704             SdrObject* pObj = pOL->GetObj(nObjNum);
705             if (!pObj)
706                 continue;
707             if (pObj == GetMarkedObjectByIndex(0))
708                 continue;
709             const SdrGluePointList* pGPL = pObj->GetGluePointList();
710             bool VertexObject = !(pGPL && pGPL->GetCount());
711             const size_t count = !VertexObject ? pGPL->GetCount() : 4;
712             boost::property_tree::ptree object;
713             boost::property_tree::ptree points;
714             for (size_t i = 0; i < count; ++i)
715             {
716                 boost::property_tree::ptree node;
717                 boost::property_tree::ptree point;
718                 const SdrGluePoint& rGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
719                 Point rPoint = rGP.GetAbsolutePos(*pObj);
720                 if (bConvertUnit)
721                     rPoint = OutputDevice::LogicToLogic(rPoint, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
722                 point.put("x", rPoint.getX());
723                 point.put("y", rPoint.getY());
724                 node.add_child("point", point);
725                 points.push_back(std::make_pair("", node));
726             }
727             basegfx::B2DVector aGridOffset(0.0, 0.0);
728             Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
729             if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
730             {
731                 Point p(aGridOffset.getX(), aGridOffset.getY());
732                 if (bConvertUnit)
733                     p = OutputDevice::LogicToLogic(p, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
734                 boost::property_tree::ptree gridOffset;
735                 gridOffset.put("x", p.getX());
736                 gridOffset.put("y", p.getY());
737                 object.add_child("gridoffset", gridOffset);
738             }
739             object.put("ordnum", pObj->GetOrdNum());
740             object.add_child("gluepoints", points);
741             elements.push_back(std::make_pair("", object));
742             result = true;
743         }
744         rTree.add_child("shapes", elements);
745     }
746     return result;
747 }
748 
SetMarkHandlesForLOKit(tools::Rectangle const & rRect,const SfxViewShell * pOtherShell)749 void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
750 {
751     SfxViewShell* pViewShell = GetSfxViewShell();
752 
753     tools::Rectangle aSelection(rRect);
754     bool bIsChart = false;
755     Point addLogicOffset(0, 0);
756     bool convertMapMode = false;
757     if (!rRect.IsEmpty())
758     {
759         sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
760         if (nTotalPaintWindows == 1)
761         {
762             const OutputDevice* pOut = this->GetFirstOutputDevice();
763             const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
764             if (pWin && pWin->IsChart())
765             {
766                 bIsChart = true;
767                 const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
768                 if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
769                 {
770                     Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
771                     Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
772                     addLogicOffset = aLogicOffset;
773                     aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
774                 }
775             }
776         }
777     }
778 
779     if (!aSelection.IsEmpty())
780     {
781         // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
782         if (mpMarkedPV)
783         {
784             if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
785             {
786                 if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
787                 {
788                     aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
789                     convertMapMode = true;
790                 }
791             }
792         }
793 
794         // hide the text selection too
795         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "");
796     }
797 
798     {
799         OString sSelectionText;
800         OString sSelectionTextView;
801         boost::property_tree::ptree aTableJsonTree;
802         boost::property_tree::ptree aGluePointsTree;
803         bool bTableSelection = false;
804         bool bConnectorSelection = false;
805 
806         if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == OBJ_TABLE)
807         {
808             auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
809             bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
810         }
811         if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == OBJ_EDGE)
812         {
813             bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
814         }
815         if (GetMarkedObjectCount())
816         {
817             SdrMark* pM = GetSdrMarkByIndex(0);
818             SdrObject* pO = pM->GetMarkedSdrObj();
819             Degree100 nRotAngle = pO->GetRotateAngle();
820             // true if we are dealing with a RotGrfFlyFrame
821             // (SwVirtFlyDrawObj with a SwGrfNode)
822             bool bWriterGraphic = pO->HasLimitedRotation();
823 
824             OStringBuffer aExtraInfo;
825             OString handleArrayStr;
826 
827             aExtraInfo.append("{\"id\":\"");
828             aExtraInfo.append(reinterpret_cast<sal_IntPtr>(pO));
829             aExtraInfo.append("\",\"type\":");
830             aExtraInfo.append(static_cast<sal_Int32>(pO->GetObjIdentifier()));
831 
832             // In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
833             // In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
834             // SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
835             // but the rotation is not applied to the LogicRect. Therefore,
836             // what we calculate in online does not match with the core in case of the rotation.
837             // Here we can send the correct gridOffset in the selection callback, this way
838             // whether the shape is rotated or not, we will always have the correct gridOffset
839             // Note that the gridOffset is calculated from the first selected obj
840             basegfx::B2DVector aGridOffset(0.0, 0.0);
841             if(getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
842             {
843                 Point p(aGridOffset.getX(), aGridOffset.getY());
844                 if (convertMapMode)
845                     p = OutputDevice::LogicToLogic(p, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
846                 aExtraInfo.append(",\"gridOffsetX\":");
847                 aExtraInfo.append(p.getX());
848                 aExtraInfo.append(",\"gridOffsetY\":");
849                 aExtraInfo.append(p.getY());
850             }
851 
852             if (bWriterGraphic)
853             {
854                 aExtraInfo.append(", \"isWriterGraphic\": true");
855             }
856             else if (bIsChart)
857             {
858                 LokChartHelper aChartHelper(pViewShell);
859                 css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
860                 css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
861                 if (xSelectionSupplier.is())
862                 {
863                     uno::Any aSel = xSelectionSupplier->getSelection();
864                     OUString aValue;
865                     if (aSel >>= aValue)
866                     {
867                         OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
868                         const std::vector<OString> aProps{"Draggable", "Resizable", "Rotatable"};
869                         for (const auto& rProp: aProps)
870                         {
871                             sal_Int32 nPos = aObjectCID.indexOf(rProp);
872                             if (nPos == -1) continue;
873                             nPos += rProp.getLength() + 1; // '='
874                             if (aExtraInfo.getLength() > 2) // != "{ "
875                                 aExtraInfo.append(", ");
876                             aExtraInfo.append("\"is");
877                             aExtraInfo.append(rProp);
878                             aExtraInfo.append("\": ");
879                             aExtraInfo.append(OString::boolean(aObjectCID[nPos] == '1'));
880                         }
881 
882                         OUString sDragMethod = lcl_getDragMethodServiceName(aValue);
883                         if (sDragMethod == "PieSegmentDragging")
884                         {
885                             // old initial offset inside the CID returned by xSelectionSupplier->getSelection()
886                             // after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
887                             aValue = pO->GetName();
888                             OUString sDragParameters = lcl_getDragParameterString(aValue);
889                             if (!sDragParameters.isEmpty())
890                             {
891                                 aExtraInfo.append(", \"dragInfo\": { ");
892                                 aExtraInfo.append("\"dragMethod\": \"");
893                                 aExtraInfo.append(sDragMethod.toUtf8());
894                                 aExtraInfo.append("\"");
895 
896                                 OUString sParam;
897                                 sal_Int32 nStartIndex = 0;
898                                 std::array<int, 5> aDragParameters;
899                                 for (auto& rParam : aDragParameters)
900                                 {
901                                     sParam = sDragParameters.getToken(0, ',', nStartIndex);
902                                     if (sParam.isEmpty())
903                                         break;
904                                     rParam = sParam.toInt32();
905                                 }
906 
907                                 // initial offset in %
908                                 if (aDragParameters[0] < 0)
909                                     aDragParameters[0] = 0;
910                                 else if (aDragParameters[0] > 100)
911                                     aDragParameters[0] = 100;
912 
913                                 aExtraInfo.append(", \"initialOffset\": ");
914                                 aExtraInfo.append(static_cast<sal_Int32>(aDragParameters[0]));
915 
916                                 // drag direction constraint
917                                 Point aMinPos(aDragParameters[1], aDragParameters[2]);
918                                 Point aMaxPos(aDragParameters[3], aDragParameters[4]);
919                                 Point aDragDirection = aMaxPos - aMinPos;
920                                 aDragDirection = OutputDevice::LogicToLogic(aDragDirection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
921 
922                                 aExtraInfo.append(", \"dragDirection\": [");
923                                 aExtraInfo.append(aDragDirection.toString());
924                                 aExtraInfo.append("]");
925 
926                                 // polygon approximating the pie segment or donut segment
927                                 if (pO->GetObjIdentifier() == OBJ_PATHFILL)
928                                 {
929                                     const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
930                                     if (aPolyPolygon.count() == 1)
931                                     {
932                                         const basegfx::B2DPolygon aPolygon = aPolyPolygon.getB2DPolygon(0);
933                                         if (sal_uInt32 nPolySize = aPolygon.count())
934                                         {
935                                             const OutputDevice* pOut = this->GetFirstOutputDevice();
936                                             const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
937                                             const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
938                                             if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
939                                             {
940                                                 // in the following code escaping sequences used inside raw literal strings
941                                                 // are for making them understandable by the JSON parser
942 
943                                                 Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
944                                                 Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
945                                                 OString sPolygonElem("<polygon points=\\\"");
946                                                 for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
947                                                 {
948                                                     const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
949                                                     Point aPoint(aB2Point.getX(), aB2Point.getY());
950                                                     aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
951                                                     if (nIndex > 0)
952                                                         sPolygonElem += " ";
953                                                     sPolygonElem += aPoint.toString();
954                                                 }
955                                                 sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";
956 
957                                                 OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
958                                                     OString::number(aSelection.GetWidth() / 100.0) +
959                                                     R"elem(mm\" height=\")elem" +
960                                                     OString::number(aSelection.GetHeight() / 100.0) +
961                                                     R"elem(mm\" viewBox=\")elem" +
962                                                     aSelection.toString() +
963                                                     R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";
964 
965                                                 aExtraInfo.append(", \"svg\": \"");
966                                                 aExtraInfo.append(sSVGElem);
967                                                 aExtraInfo.append("\\n  ");
968                                                 aExtraInfo.append(sPolygonElem);
969                                                 aExtraInfo.append("\\n</svg>");
970                                                 aExtraInfo.append("\""); // svg
971                                             }
972                                         }
973                                     }
974                                 }
975                                 aExtraInfo.append("}"); // dragInfo
976                             }
977                         }
978                     }
979                 }
980             }
981             if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
982             {
983                 boost::property_tree::ptree responseJSON;
984                 boost::property_tree::ptree others;
985                 boost::property_tree::ptree anchor;
986                 boost::property_tree::ptree rectangle;
987                 boost::property_tree::ptree poly;
988                 boost::property_tree::ptree custom;
989                 boost::property_tree::ptree nodes;
990                 for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
991                 {
992                     SdrHdl *pHdl = maHdlList.GetHdl(i);
993                     boost::property_tree::ptree child;
994                     boost::property_tree::ptree point;
995                     sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
996                     child.put("id", pHdl->GetObjHdlNum());
997                     child.put("kind", kind);
998                     child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
999                     Point pHdlPos = pHdl->GetPos();
1000                     pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
1001                     if (convertMapMode)
1002                         pHdlPos = OutputDevice::LogicToLogic(pHdlPos, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
1003                     point.put("x", pHdlPos.getX());
1004                     point.put("y", pHdlPos.getY());
1005                     child.add_child("point", point);
1006                     const auto node = std::make_pair("", child);
1007                     boost::property_tree::ptree* selectedNode = nullptr;
1008                     if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
1009                     {
1010                         selectedNode = &rectangle;
1011                     }
1012                     else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
1013                     {
1014                         selectedNode = &poly;
1015                     }
1016                     else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
1017                     {
1018                         selectedNode = &custom;
1019                     }
1020                     else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
1021                     {
1022                         if (getSdrModelFromSdrView().IsWriter())
1023                             selectedNode = &anchor;
1024                         else
1025                             // put it to others as we don't render them except in writer
1026                             selectedNode = &others;
1027                     }
1028                     else
1029                     {
1030                         selectedNode = &others;
1031                     }
1032                     std::string sKind = std::to_string(kind);
1033                     boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
1034                     if (!kindNode)
1035                     {
1036                         boost::property_tree::ptree newChild;
1037                         newChild.push_back(node);
1038                         selectedNode->add_child(sKind.c_str(), newChild);
1039                     }
1040                     else
1041                         kindNode.get().push_back(node);
1042                 }
1043                 nodes.add_child("rectangle", rectangle);
1044                 nodes.add_child("poly", poly);
1045                 nodes.add_child("custom", custom);
1046                 nodes.add_child("anchor", anchor);
1047                 nodes.add_child("others", others);
1048                 responseJSON.add_child("kinds", nodes);
1049                 std::stringstream aStream;
1050                 boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
1051                 handleArrayStr = ", \"handles\":";
1052                 handleArrayStr = handleArrayStr + aStream.str().c_str();
1053                 if (bConnectorSelection)
1054                 {
1055                     aStream.str("");
1056                     boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
1057                     handleArrayStr = handleArrayStr + ", \"GluePoints\":";
1058                     handleArrayStr = handleArrayStr + aStream.str().c_str();
1059                 }
1060             }
1061             sSelectionText = aSelection.toString() +
1062                 ", " + OString::number(nRotAngle.get());
1063             if (!aExtraInfo.isEmpty())
1064             {
1065                 sSelectionTextView = sSelectionText + ", " + aExtraInfo.toString() + "}";
1066                 aExtraInfo.append(handleArrayStr);
1067                 aExtraInfo.append("}");
1068                 sSelectionText += ", " + aExtraInfo.makeStringAndClear();
1069             }
1070         }
1071 
1072         if (sSelectionText.isEmpty())
1073         {
1074             sSelectionText = "EMPTY";
1075             sSelectionTextView = "EMPTY";
1076         }
1077 
1078         if (bTableSelection)
1079         {
1080             boost::property_tree::ptree aTableRectangle;
1081             aTableRectangle.put("x", aSelection.Left());
1082             aTableRectangle.put("y", aSelection.Top());
1083             aTableRectangle.put("width", aSelection.GetWidth());
1084             aTableRectangle.put("height", aSelection.GetHeight());
1085             aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));
1086 
1087             std::stringstream aStream;
1088             boost::property_tree::write_json(aStream, aTableJsonTree);
1089             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, aStream.str().c_str());
1090         }
1091         else
1092         {
1093             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}");
1094         }
1095 
1096         if (pOtherShell)
1097         {
1098             // Another shell wants to know about our existing
1099             // selection.
1100             if (pViewShell != pOtherShell)
1101                 SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
1102         }
1103         else
1104         {
1105             // We have a new selection, so both pViewShell and the
1106             // other views want to know about it.
1107             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText.getStr());
1108             SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
1109         }
1110     }
1111 }
1112 
SetMarkHandles(SfxViewShell * pOtherShell)1113 void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
1114 {
1115     // remember old focus handle values to search for it again
1116     const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
1117     bool bSaveOldFocus(false);
1118     sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
1119     SdrHdlKind eSaveKind(SdrHdlKind::Move);
1120     SdrObject* pSaveObj = nullptr;
1121 
1122     mpMarkingSubSelectionOverlay.reset();
1123 
1124     if(pSaveOldFocusHdl
1125         && pSaveOldFocusHdl->GetObj()
1126         && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
1127         && (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
1128     {
1129         bSaveOldFocus = true;
1130         nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
1131         nSavePointNum = pSaveOldFocusHdl->GetPointNum();
1132         pSaveObj = pSaveOldFocusHdl->GetObj();
1133         eSaveKind = pSaveOldFocusHdl->GetKind();
1134     }
1135 
1136     // delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
1137     maHdlList.Clear();
1138     maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
1139     maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
1140     mpMarkedObj=nullptr;
1141     mpMarkedPV=nullptr;
1142 
1143     // are handles enabled at all? Create only then
1144     if(areMarkHandlesHidden())
1145         return;
1146 
1147     // There can be multiple mark views, but we're only interested in the one that has a window associated.
1148     const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;
1149 
1150     const size_t nMarkCount=GetMarkedObjectCount();
1151     bool bStdDrag=meDragMode==SdrDragMode::Move;
1152     bool bSingleTextObjMark=false;
1153     bool bLimitedRotation(false);
1154 
1155     if (nMarkCount==1)
1156     {
1157         mpMarkedObj=GetMarkedObjectByIndex(0);
1158 
1159         if(nullptr != mpMarkedObj)
1160         {
1161             bSingleTextObjMark =
1162                 dynamic_cast<const SdrTextObj*>( mpMarkedObj) !=  nullptr &&
1163                 static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
1164 
1165             // RotGrfFlyFrame: we may have limited rotation
1166             bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
1167         }
1168     }
1169 
1170     bool bFrmHdl=ImpIsFrameHandles();
1171 
1172     if (nMarkCount>0)
1173     {
1174         mpMarkedPV=GetSdrPageViewOfMarkedByIndex(0);
1175 
1176         for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
1177         {
1178             const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1179 
1180             if (mpMarkedPV!=pM->GetPageView())
1181             {
1182                 mpMarkedPV=nullptr;
1183             }
1184         }
1185     }
1186 
1187     SfxViewShell* pViewShell = GetSfxViewShell();
1188 
1189     // check if text edit or ole is active and handles need to be suppressed. This may be the case
1190     // when a single object is selected
1191     // Using a strict return statement is okay here; no handles means *no* handles.
1192     if(mpMarkedObj)
1193     {
1194         // formerly #i33755#: If TextEdit is active the EditEngine will directly paint
1195         // to the window, so suppress Overlay and handles completely; a text frame for
1196         // the active text edit will be painted by the repaint mechanism in
1197         // SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
1198         // in the future
1199         // Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
1200         if(static_cast<SdrView*>(this)->IsTextEdit())
1201         {
1202             const SdrTextObj* pSdrTextObj = dynamic_cast< const SdrTextObj* >(mpMarkedObj);
1203 
1204             if (pSdrTextObj && pSdrTextObj->IsInEditMode())
1205             {
1206                 if (!bTiledRendering)
1207                     return;
1208             }
1209         }
1210 
1211         // formerly #i118524#: if inplace activated OLE is selected, suppress handles
1212         const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);
1213 
1214         if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
1215         {
1216             return;
1217         }
1218 
1219         if (!maSubSelectionList.empty())
1220         {
1221             mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
1222         }
1223     }
1224 
1225     tools::Rectangle aRect(GetMarkedObjRect());
1226 
1227     if (bFrmHdl)
1228     {
1229         if(!aRect.IsEmpty())
1230         {
1231             // otherwise nothing is found
1232             const size_t nSiz0(maHdlList.GetHdlCount());
1233 
1234             if( bSingleTextObjMark )
1235             {
1236                 mpMarkedObj->AddToHdlList(maHdlList);
1237             }
1238             else
1239             {
1240                 const bool bWdt0(aRect.Left() == aRect.Right());
1241                 const bool bHgt0(aRect.Top() == aRect.Bottom());
1242 
1243                 if (bWdt0 && bHgt0)
1244                 {
1245                     maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1246                 }
1247                 else if (!bStdDrag && (bWdt0 || bHgt0))
1248                 {
1249                     maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1250                     maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
1251                 }
1252                 else
1253                 {
1254                     if (!bWdt0 && !bHgt0)
1255                     {
1256                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
1257                     }
1258 
1259                     if (!bLimitedRotation && !bHgt0)
1260                     {
1261                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
1262                     }
1263 
1264                     if (!bWdt0 && !bHgt0)
1265                     {
1266                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
1267                     }
1268 
1269                     if (!bLimitedRotation && !bWdt0)
1270                     {
1271                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
1272                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
1273                     }
1274 
1275                     if (!bWdt0 && !bHgt0)
1276                     {
1277                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
1278                     }
1279 
1280                     if (!bLimitedRotation && !bHgt0)
1281                     {
1282                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
1283                     }
1284 
1285                     if (!bWdt0 && !bHgt0)
1286                     {
1287                         maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
1288                     }
1289                 }
1290             }
1291 
1292             const size_t nSiz1(maHdlList.GetHdlCount());
1293 
1294             // moved setting the missing parameters at SdrHdl here from the
1295             // single loop above (bSingleTextObjMark), this was missing all
1296             // the time. Setting SdrObject is now required to correctly get
1297             // the View-Dependent evtl. GridOffset adapted
1298             for (size_t i=nSiz0; i<nSiz1; ++i)
1299             {
1300                 SdrHdl* pHdl=maHdlList.GetHdl(i);
1301                 pHdl->SetObj(mpMarkedObj);
1302                 pHdl->SetPageView(mpMarkedPV);
1303                 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1304             }
1305         }
1306     }
1307     else
1308     {
1309         bool bDone(false);
1310 
1311         // moved crop handling to non-frame part and the handle creation to SdrGrafObj
1312         if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
1313         {
1314             // Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
1315             // behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
1316             // writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
1317             const size_t nSiz0(maHdlList.GetHdlCount());
1318             mpMarkedObj->addCropHandles(maHdlList);
1319             const size_t nSiz1(maHdlList.GetHdlCount());
1320 
1321             // Was missing: Set infos at SdrCropHdl
1322             for (size_t i=nSiz0; i<nSiz1; ++i)
1323             {
1324                 SdrHdl* pHdl=maHdlList.GetHdl(i);
1325                 pHdl->SetObj(mpMarkedObj);
1326                 pHdl->SetPageView(mpMarkedPV);
1327                 pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1328             }
1329 
1330             bDone = true;
1331         }
1332 
1333         if(!bDone)
1334         {
1335             for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
1336             {
1337                 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1338                 SdrObject* pObj=pM->GetMarkedSdrObj();
1339                 SdrPageView* pPV=pM->GetPageView();
1340                 const size_t nSiz0=maHdlList.GetHdlCount();
1341                 pObj->AddToHdlList(maHdlList);
1342                 const size_t nSiz1=maHdlList.GetHdlCount();
1343                 bool bPoly=pObj->IsPolyObj();
1344                 const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
1345                 for (size_t i=nSiz0; i<nSiz1; ++i)
1346                 {
1347                     SdrHdl* pHdl=maHdlList.GetHdl(i);
1348                     pHdl->SetObj(pObj);
1349                     pHdl->SetPageView(pPV);
1350                     pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
1351 
1352                     if (bPoly)
1353                     {
1354                         bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
1355                         pHdl->SetSelected(bSelected);
1356                         if (mbPlusHdlAlways || bSelected)
1357                         {
1358                             SdrHdlList plusList(nullptr);
1359                             pObj->AddToPlusHdlList(plusList, *pHdl);
1360                             sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
1361                             for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
1362                             {
1363                                 SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
1364                                 pPlusHdl->SetObj(pObj);
1365                                 pPlusHdl->SetPageView(pPV);
1366                                 pPlusHdl->SetPlusHdl(true);
1367                             }
1368                             plusList.MoveTo(maHdlList);
1369                         }
1370                     }
1371                 }
1372             }
1373         }
1374     }
1375 
1376     // GluePoint handles
1377     for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
1378     {
1379         const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
1380         SdrObject* pObj=pM->GetMarkedSdrObj();
1381         const SdrGluePointList* pGPL=pObj->GetGluePointList();
1382         if (!pGPL)
1383             continue;
1384 
1385         SdrPageView* pPV=pM->GetPageView();
1386         const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
1387         for (sal_uInt16 nId : rMrkGlue)
1388         {
1389             //nNum changed to nNumGP because already used in for loop
1390             sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
1391             if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
1392             {
1393                 const SdrGluePoint& rGP=(*pGPL)[nNumGP];
1394                 Point aPos(rGP.GetAbsolutePos(*pObj));
1395                 std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
1396                 pGlueHdl->SetObj(pObj);
1397                 pGlueHdl->SetPageView(pPV);
1398                 pGlueHdl->SetObjHdlNum(nId);
1399                 maHdlList.AddHdl(std::move(pGlueHdl));
1400             }
1401         }
1402     }
1403 
1404     // rotation point/axis of reflection
1405     if(!bLimitedRotation)
1406     {
1407         AddDragModeHdl(meDragMode);
1408     }
1409 
1410     // sort handles
1411     maHdlList.Sort();
1412 
1413     // add custom handles (used by other apps, e.g. AnchorPos)
1414     AddCustomHdl();
1415 
1416     // moved it here to access all the handles for callback.
1417     if (bTiledRendering && pViewShell)
1418     {
1419         SetMarkHandlesForLOKit(aRect, pOtherShell);
1420     }
1421 
1422     // try to restore focus handle index from remembered values
1423     if(!bSaveOldFocus)
1424         return;
1425 
1426     for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
1427     {
1428         SdrHdl* pCandidate = maHdlList.GetHdl(a);
1429 
1430         if(pCandidate->GetObj()
1431             && pCandidate->GetObj() == pSaveObj
1432             && pCandidate->GetKind() == eSaveKind
1433             && pCandidate->GetPolyNum() == nSavePolyNum
1434             && pCandidate->GetPointNum() == nSavePointNum)
1435         {
1436             maHdlList.SetFocusHdl(pCandidate);
1437             break;
1438         }
1439     }
1440 }
1441 
AddCustomHdl()1442 void SdrMarkView::AddCustomHdl()
1443 {
1444     // add custom handles (used by other apps, e.g. AnchorPos)
1445 }
1446 
SetDragMode(SdrDragMode eMode)1447 void SdrMarkView::SetDragMode(SdrDragMode eMode)
1448 {
1449     SdrDragMode eMode0=meDragMode;
1450     meDragMode=eMode;
1451     if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
1452     if (meDragMode!=eMode0) {
1453         ForceRefToMarked();
1454         SetMarkHandles(nullptr);
1455         {
1456             if (AreObjectsMarked()) MarkListHasChanged();
1457         }
1458     }
1459 }
1460 
AddDragModeHdl(SdrDragMode eMode)1461 void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
1462 {
1463     switch(eMode)
1464     {
1465         case SdrDragMode::Rotate:
1466         {
1467             // add rotation center
1468             maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
1469             break;
1470         }
1471         case SdrDragMode::Mirror:
1472         {
1473             // add axis of reflection
1474             std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
1475             std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
1476             std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));
1477 
1478             pHdl1->SetObjHdlNum(1); // for sorting
1479             pHdl2->SetObjHdlNum(2); // for sorting
1480             pHdl3->SetObjHdlNum(3); // for sorting
1481 
1482             maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
1483             maHdlList.AddHdl(std::move(pHdl2));
1484             maHdlList.AddHdl(std::move(pHdl3));
1485 
1486             break;
1487         }
1488         case SdrDragMode::Transparence:
1489         {
1490             // add interactive transparency handle
1491             const size_t nMarkCount = GetMarkedObjectCount();
1492             if(nMarkCount == 1)
1493             {
1494                 SdrObject* pObj = GetMarkedObjectByIndex(0);
1495                 SdrModel* pModel = GetModel();
1496                 const SfxItemSet& rSet = pObj->GetMergedItemSet();
1497 
1498                 if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
1499                 {
1500                     // add this item, it's not yet there
1501                     XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
1502                     XGradient aGrad = aNewItem.GetGradientValue();
1503 
1504                     aNewItem.SetEnabled(true);
1505                     aGrad.SetStartIntens(100);
1506                     aGrad.SetEndIntens(100);
1507                     aNewItem.SetGradientValue(aGrad);
1508 
1509                     // add undo to allow user to take back this step
1510                     if( pModel->IsUndoEnabled() )
1511                     {
1512                         pModel->BegUndo(SvxResId(SIP_XA_FILLTRANSPARENCE));
1513                         pModel->AddUndo(pModel->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
1514                         pModel->EndUndo();
1515                     }
1516 
1517                     SfxItemSet aNewSet(pModel->GetItemPool());
1518                     aNewSet.Put(aNewItem);
1519                     pObj->SetMergedItemSetAndBroadcast(aNewSet);
1520                 }
1521 
1522                 // set values and transform to vector set
1523                 GradTransVector aGradTransVector;
1524                 GradTransGradient aGradTransGradient;
1525 
1526                 aGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
1527                 GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
1528 
1529                 // build handles
1530                 const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
1531                 const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
1532                 std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
1533                 std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
1534                 std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, false));
1535                 DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
1536 
1537                 // link them
1538                 pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
1539                 pGradHdl->SetObj(pObj);
1540                 pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1541                 pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1542 
1543                 // insert them
1544                 maHdlList.AddHdl(std::move(pColHdl1));
1545                 maHdlList.AddHdl(std::move(pColHdl2));
1546                 maHdlList.AddHdl(std::move(pGradHdl));
1547             }
1548             break;
1549         }
1550         case SdrDragMode::Gradient:
1551         {
1552             // add interactive gradient handle
1553             const size_t nMarkCount = GetMarkedObjectCount();
1554             if(nMarkCount == 1)
1555             {
1556                 SdrObject* pObj = GetMarkedObjectByIndex(0);
1557                 const SfxItemSet& rSet = pObj->GetMergedItemSet();
1558                 drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
1559 
1560                 if(eFillStyle == drawing::FillStyle_GRADIENT)
1561                 {
1562                     // set values and transform to vector set
1563                     GradTransVector aGradTransVector;
1564                     GradTransGradient aGradTransGradient;
1565                     Size aHdlSize(15, 15);
1566 
1567                     aGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
1568                     GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
1569 
1570                     // build handles
1571                     const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
1572                     const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
1573                     std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, aHdlSize, false));
1574                     std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, aHdlSize, false));
1575                     std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, true));
1576                     DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
1577 
1578                     // link them
1579                     pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
1580                     pGradHdl->SetObj(pObj);
1581                     pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1582                     pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
1583 
1584                     // insert them
1585                     maHdlList.AddHdl(std::move(pColHdl1));
1586                     maHdlList.AddHdl(std::move(pColHdl2));
1587                     maHdlList.AddHdl(std::move(pGradHdl));
1588                 }
1589             }
1590             break;
1591         }
1592         case SdrDragMode::Crop:
1593         {
1594             // TODO
1595             break;
1596         }
1597         default: break;
1598     }
1599 }
1600 
1601 /** handle mouse over effects for handles */
MouseMove(const MouseEvent & rMEvt,OutputDevice * pWin)1602 bool SdrMarkView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
1603 {
1604     if(maHdlList.GetHdlCount())
1605     {
1606         SdrHdl* pMouseOverHdl = nullptr;
1607         if( !rMEvt.IsLeaveWindow() && pWin )
1608         {
1609             Point aMDPos( pWin->PixelToLogic( rMEvt.GetPosPixel() ) );
1610             pMouseOverHdl = PickHandle(aMDPos);
1611         }
1612 
1613         // notify last mouse over handle that he lost the mouse
1614         const size_t nHdlCount = maHdlList.GetHdlCount();
1615 
1616         for(size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
1617         {
1618             SdrHdl* pCurrentHdl = GetHdl(nHdl);
1619             if( pCurrentHdl->mbMouseOver )
1620             {
1621                 if( pCurrentHdl != pMouseOverHdl )
1622                 {
1623                     pCurrentHdl->mbMouseOver = false;
1624                     pCurrentHdl->onMouseLeave();
1625                 }
1626                 break;
1627             }
1628         }
1629 
1630         // notify current mouse over handle
1631         if( pMouseOverHdl )
1632         {
1633             pMouseOverHdl->mbMouseOver = true;
1634             pMouseOverHdl->onMouseEnter(rMEvt);
1635         }
1636     }
1637     return SdrSnapView::MouseMove(rMEvt, pWin);
1638 }
1639 
RequestHelp(const HelpEvent & rHEvt)1640 bool SdrMarkView::RequestHelp(const HelpEvent& rHEvt)
1641 {
1642     if (maHdlList.GetHdlCount())
1643     {
1644         const size_t nHdlCount = maHdlList.GetHdlCount();
1645 
1646         for (size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
1647         {
1648             SdrHdl* pCurrentHdl = GetHdl(nHdl);
1649             if (pCurrentHdl->mbMouseOver)
1650             {
1651                 pCurrentHdl->onHelpRequest();
1652                 return true;
1653             }
1654         }
1655     }
1656     return SdrSnapView::RequestHelp(rHEvt);
1657 }
1658 
ForceRefToMarked()1659 void SdrMarkView::ForceRefToMarked()
1660 {
1661     switch(meDragMode)
1662     {
1663         case SdrDragMode::Rotate:
1664         {
1665             tools::Rectangle aR(GetMarkedObjRect());
1666             maRef1 = aR.Center();
1667 
1668             break;
1669         }
1670 
1671         case SdrDragMode::Mirror:
1672         {
1673             // first calculate the length of the axis of reflection
1674             tools::Long nOutMin=0;
1675             tools::Long nOutMax=0;
1676             tools::Long nMinLen=0;
1677             tools::Long nObjDst=0;
1678             tools::Long nOutHgt=0;
1679             OutputDevice* pOut=GetFirstOutputDevice();
1680             if (pOut!=nullptr) {
1681                 // minimum length: 50 pixels
1682                 nMinLen=pOut->PixelToLogic(Size(0,50)).Height();
1683                 // 20 pixels distance to the Obj for the reference point
1684                 nObjDst=pOut->PixelToLogic(Size(0,20)).Height();
1685                 // MinY/MaxY
1686                 // margin = minimum length = 10 pixels
1687                 tools::Long nDst=pOut->PixelToLogic(Size(0,10)).Height();
1688                 nOutMin=-pOut->GetMapMode().GetOrigin().Y();
1689                 nOutMax=pOut->GetOutputSize().Height()-1+nOutMin;
1690                 nOutMin+=nDst;
1691                 nOutMax-=nDst;
1692                 // absolute minimum length, however, is 10 pixels
1693                 if (nOutMax-nOutMin<nDst) {
1694                     nOutMin+=nOutMax+1;
1695                     nOutMin/=2;
1696                     nOutMin-=(nDst+1)/2;
1697                     nOutMax=nOutMin+nDst;
1698                 }
1699                 nOutHgt=nOutMax-nOutMin;
1700                 // otherwise minimum length = 1/4 OutHgt
1701                 tools::Long nTemp=nOutHgt/4;
1702                 if (nTemp>nMinLen) nMinLen=nTemp;
1703             }
1704 
1705             tools::Rectangle aR(GetMarkedObjBoundRect());
1706             Point aCenter(aR.Center());
1707             tools::Long nMarkHgt=aR.GetHeight()-1;
1708             tools::Long nHgt=nMarkHgt+nObjDst*2;       // 20 pixels overlapping above and below
1709             if (nHgt<nMinLen) nHgt=nMinLen;     // minimum length 50 pixels or 1/4 OutHgt, respectively
1710 
1711             tools::Long nY1=aCenter.Y()-(nHgt+1)/2;
1712             tools::Long nY2=nY1+nHgt;
1713 
1714             if (pOut!=nullptr && nMinLen>nOutHgt) nMinLen=nOutHgt; // TODO: maybe shorten this a little
1715 
1716             if (pOut!=nullptr) { // now move completely into the visible area
1717                 if (nY1<nOutMin) {
1718                     nY1=nOutMin;
1719                     if (nY2<nY1+nMinLen) nY2=nY1+nMinLen;
1720                 }
1721                 if (nY2>nOutMax) {
1722                     nY2=nOutMax;
1723                     if (nY1>nY2-nMinLen) nY1=nY2-nMinLen;
1724                 }
1725             }
1726 
1727             maRef1.setX(aCenter.X() );
1728             maRef1.setY(nY1 );
1729             maRef2.setX(aCenter.X() );
1730             maRef2.setY(nY2 );
1731 
1732             break;
1733         }
1734 
1735         case SdrDragMode::Transparence:
1736         case SdrDragMode::Gradient:
1737         case SdrDragMode::Crop:
1738         {
1739             tools::Rectangle aRect(GetMarkedObjBoundRect());
1740             maRef1 = aRect.TopLeft();
1741             maRef2 = aRect.BottomRight();
1742             break;
1743         }
1744         default: break;
1745     }
1746 }
1747 
SetRef1(const Point & rPt)1748 void SdrMarkView::SetRef1(const Point& rPt)
1749 {
1750     if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
1751     {
1752         maRef1 = rPt;
1753         SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref1);
1754         if(pH)
1755             pH->SetPos(rPt);
1756     }
1757 }
1758 
SetRef2(const Point & rPt)1759 void SdrMarkView::SetRef2(const Point& rPt)
1760 {
1761     if(meDragMode == SdrDragMode::Mirror)
1762     {
1763         maRef2 = rPt;
1764         SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref2);
1765         if(pH)
1766             pH->SetPos(rPt);
1767     }
1768 }
1769 
GetSfxViewShell() const1770 SfxViewShell* SdrMarkView::GetSfxViewShell() const
1771 {
1772     return SfxViewShell::Current();
1773 }
1774 
CheckMarked()1775 void SdrMarkView::CheckMarked()
1776 {
1777     for (size_t nm=GetMarkedObjectCount(); nm>0;) {
1778         --nm;
1779         SdrMark* pM = GetSdrMarkByIndex(nm);
1780         SdrObject* pObj = pM->GetMarkedSdrObj();
1781         SdrPageView* pPV = pM->GetPageView();
1782         bool bRaus = !pObj || !pPV->IsObjMarkable(pObj);
1783         if (bRaus)
1784         {
1785             GetMarkedObjectListWriteAccess().DeleteMark(nm);
1786         }
1787         else
1788         {
1789             if (!IsGluePointEditMode()) { // selected glue points only in GlueEditMode
1790                 SdrUShortCont& rPts = pM->GetMarkedGluePoints();
1791                 rPts.clear();
1792             }
1793         }
1794     }
1795 
1796     // at least reset the remembered BoundRect to prevent handle
1797     // generation if bForceFrameHandles is TRUE.
1798     mbMarkedObjRectDirty = true;
1799 }
1800 
SetMarkRects()1801 void SdrMarkView::SetMarkRects()
1802 {
1803     SdrPageView* pPV = GetSdrPageView();
1804 
1805     if(pPV)
1806     {
1807         pPV->SetHasMarkedObj(GetMarkedObjectList().TakeSnapRect(pPV, pPV->MarkSnap()));
1808         GetMarkedObjectList().TakeBoundRect(pPV, pPV->MarkBound());
1809     }
1810 }
1811 
SetFrameHandles(bool bOn)1812 void SdrMarkView::SetFrameHandles(bool bOn)
1813 {
1814     if (bOn!=mbForceFrameHandles) {
1815         bool bOld=ImpIsFrameHandles();
1816         mbForceFrameHandles=bOn;
1817         bool bNew=ImpIsFrameHandles();
1818         if (bNew!=bOld) {
1819             AdjustMarkHdl();
1820             MarkListHasChanged();
1821         }
1822     }
1823 }
1824 
SetEditMode(SdrViewEditMode eMode)1825 void SdrMarkView::SetEditMode(SdrViewEditMode eMode)
1826 {
1827     if (eMode==meEditMode)        return;
1828 
1829     bool bGlue0=meEditMode==SdrViewEditMode::GluePointEdit;
1830     bool bEdge0=static_cast<SdrCreateView*>(this)->IsEdgeTool();
1831     meEditMode0=meEditMode;
1832     meEditMode=eMode;
1833     bool bGlue1=meEditMode==SdrViewEditMode::GluePointEdit;
1834     bool bEdge1=static_cast<SdrCreateView*>(this)->IsEdgeTool();
1835     // avoid flickering when switching between GlueEdit and EdgeTool
1836     if (bGlue1 && !bGlue0) ImpSetGlueVisible2(bGlue1);
1837     if (bEdge1!=bEdge0) ImpSetGlueVisible3(bEdge1);
1838     if (!bGlue1 && bGlue0) ImpSetGlueVisible2(bGlue1);
1839     if (bGlue0 && !bGlue1) UnmarkAllGluePoints();
1840 }
1841 
1842 
IsObjMarkable(SdrObject const * pObj,SdrPageView const * pPV) const1843 bool SdrMarkView::IsObjMarkable(SdrObject const * pObj, SdrPageView const * pPV) const
1844 {
1845     if (pObj)
1846     {
1847         if (pObj->IsMarkProtect() ||
1848             (!mbDesignMode && pObj->IsUnoObj()))
1849         {
1850             // object not selectable or
1851             // SdrUnoObj not in DesignMode
1852             return false;
1853         }
1854     }
1855     return pPV==nullptr || pPV->IsObjMarkable(pObj);
1856 }
1857 
IsMarkedObjHit(const Point & rPnt,short nTol) const1858 bool SdrMarkView::IsMarkedObjHit(const Point& rPnt, short nTol) const
1859 {
1860     bool bRet=false;
1861     nTol=ImpGetHitTolLogic(nTol,nullptr);
1862     for (size_t nm=0; nm<GetMarkedObjectCount() && !bRet; ++nm) {
1863         SdrMark* pM=GetSdrMarkByIndex(nm);
1864         bRet = nullptr != CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr);
1865     }
1866     return bRet;
1867 }
1868 
PickHandle(const Point & rPnt) const1869 SdrHdl* SdrMarkView::PickHandle(const Point& rPnt) const
1870 {
1871     if (mbSomeObjChgdFlag) { // recalculate handles, if necessary
1872         FlushComeBackTimer();
1873     }
1874     return maHdlList.IsHdlListHit(rPnt);
1875 }
1876 
MarkObj(const Point & rPnt,short nTol,bool bToggle,bool bDeep)1877 bool SdrMarkView::MarkObj(const Point& rPnt, short nTol, bool bToggle, bool bDeep)
1878 {
1879     SdrPageView* pPV;
1880     nTol=ImpGetHitTolLogic(nTol,nullptr);
1881     SdrSearchOptions nOptions=SdrSearchOptions::PICKMARKABLE;
1882     if (bDeep) nOptions=nOptions|SdrSearchOptions::DEEP;
1883     SdrObject* pObj = PickObj(rPnt, static_cast<sal_uInt16>(nTol), pPV, nOptions);
1884     if (pObj) {
1885         bool bUnmark=bToggle && IsObjMarked(pObj);
1886         MarkObj(pObj,pPV,bUnmark);
1887     }
1888     return pObj != nullptr;
1889 }
1890 
MarkNextObj(bool bPrev)1891 bool SdrMarkView::MarkNextObj(bool bPrev)
1892 {
1893     SdrPageView* pPageView = GetSdrPageView();
1894 
1895     if(!pPageView)
1896     {
1897         return false;
1898     }
1899 
1900     SortMarkedObjects();
1901     const size_t nMarkCount=GetMarkedObjectCount();
1902     size_t nChgMarkNum = SAL_MAX_SIZE; // number of the MarkEntry we want to replace
1903     size_t nSearchObjNum = bPrev ? 0 : SAL_MAX_SIZE;
1904     if (nMarkCount!=0) {
1905         nChgMarkNum=bPrev ? 0 : nMarkCount-1;
1906         SdrMark* pM=GetSdrMarkByIndex(nChgMarkNum);
1907         OSL_ASSERT(pM!=nullptr);
1908         if (pM->GetMarkedSdrObj() != nullptr)
1909             nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition();
1910     }
1911 
1912     SdrObject* pMarkObj=nullptr;
1913     SdrObjList* pSearchObjList=pPageView->GetObjList();
1914     const size_t nObjCount = pSearchObjList->GetObjCount();
1915     if (nObjCount!=0) {
1916         if (nSearchObjNum>nObjCount) nSearchObjNum=nObjCount;
1917         while (pMarkObj==nullptr && ((!bPrev && nSearchObjNum>0) || (bPrev && nSearchObjNum<nObjCount)))
1918         {
1919             if (!bPrev)
1920                 nSearchObjNum--;
1921             SdrObject* pSearchObj = pSearchObjList->GetObjectForNavigationPosition(nSearchObjNum);
1922             if (IsObjMarkable(pSearchObj,pPageView))
1923             {
1924                 if (TryToFindMarkedObject(pSearchObj)==SAL_MAX_SIZE)
1925                 {
1926                     pMarkObj=pSearchObj;
1927                 }
1928             }
1929             if (bPrev) nSearchObjNum++;
1930         }
1931     }
1932 
1933     if(!pMarkObj)
1934     {
1935         return false;
1936     }
1937 
1938     if (nChgMarkNum!=SAL_MAX_SIZE)
1939     {
1940         GetMarkedObjectListWriteAccess().DeleteMark(nChgMarkNum);
1941     }
1942     MarkObj(pMarkObj,pPageView); // also calls MarkListHasChanged(), AdjustMarkHdl()
1943     return true;
1944 }
1945 
MarkNextObj(const Point & rPnt,short nTol,bool bPrev)1946 bool SdrMarkView::MarkNextObj(const Point& rPnt, short nTol, bool bPrev)
1947 {
1948     SortMarkedObjects();
1949     nTol=ImpGetHitTolLogic(nTol,nullptr);
1950     SdrMark* pTopMarkHit=nullptr;
1951     SdrMark* pBtmMarkHit=nullptr;
1952     size_t nTopMarkHit=0;
1953     size_t nBtmMarkHit=0;
1954     // find topmost of the selected objects that is hit by rPnt
1955     const size_t nMarkCount=GetMarkedObjectCount();
1956     for (size_t nm=nMarkCount; nm>0 && pTopMarkHit==nullptr;) {
1957         --nm;
1958         SdrMark* pM=GetSdrMarkByIndex(nm);
1959         if(CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr))
1960         {
1961             pTopMarkHit=pM;
1962             nTopMarkHit=nm;
1963         }
1964     }
1965     // nothing found, in this case, just select an object
1966     if (pTopMarkHit==nullptr) return MarkObj(rPnt,sal_uInt16(nTol));
1967 
1968     SdrObject* pTopObjHit=pTopMarkHit->GetMarkedSdrObj();
1969     SdrObjList* pObjList=pTopObjHit->getParentSdrObjListFromSdrObject();
1970     SdrPageView* pPV=pTopMarkHit->GetPageView();
1971     // find lowermost of the selected objects that is hit by rPnt
1972     // and is placed on the same PageView as pTopMarkHit
1973     for (size_t nm=0; nm<nMarkCount && pBtmMarkHit==nullptr; ++nm) {
1974         SdrMark* pM=GetSdrMarkByIndex(nm);
1975         SdrPageView* pPV2=pM->GetPageView();
1976         if (pPV2==pPV && CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pPV2,SdrSearchOptions::NONE,nullptr))
1977         {
1978             pBtmMarkHit=pM;
1979             nBtmMarkHit=nm;
1980         }
1981     }
1982     if (pBtmMarkHit==nullptr) { pBtmMarkHit=pTopMarkHit; nBtmMarkHit=nTopMarkHit; }
1983     SdrObject* pBtmObjHit=pBtmMarkHit->GetMarkedSdrObj();
1984     const size_t nObjCount = pObjList->GetObjCount();
1985 
1986     size_t nSearchBeg(0);
1987     E3dScene* pScene(nullptr);
1988     SdrObject* pObjHit(bPrev ? pBtmObjHit : pTopObjHit);
1989     bool bRemap =
1990         nullptr != dynamic_cast< const E3dCompoundObject* >(pObjHit);
1991     if (bRemap)
1992     {
1993         pScene = dynamic_cast< E3dScene* >(pObjHit->getParentSdrObjectFromSdrObject());
1994         bRemap = nullptr != pScene;
1995     }
1996 
1997     if(bPrev)
1998     {
1999         sal_uInt32 nOrdNumBtm(pBtmObjHit->GetOrdNum());
2000 
2001         if(bRemap)
2002         {
2003             nOrdNumBtm = pScene->RemapOrdNum(nOrdNumBtm);
2004         }
2005 
2006         nSearchBeg = nOrdNumBtm + 1;
2007     }
2008     else
2009     {
2010         sal_uInt32 nOrdNumTop(pTopObjHit->GetOrdNum());
2011 
2012         if(bRemap)
2013         {
2014             nOrdNumTop = pScene->RemapOrdNum(nOrdNumTop);
2015         }
2016 
2017         nSearchBeg = nOrdNumTop;
2018     }
2019 
2020     size_t no=nSearchBeg;
2021     SdrObject* pFndObj=nullptr;
2022     while (pFndObj==nullptr && ((!bPrev && no>0) || (bPrev && no<nObjCount))) {
2023         if (!bPrev) no--;
2024         SdrObject* pObj;
2025 
2026         if(bRemap)
2027         {
2028             pObj = pObjList->GetObj(pScene->RemapOrdNum(no));
2029         }
2030         else
2031         {
2032             pObj = pObjList->GetObj(no);
2033         }
2034 
2035         if (CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr))
2036         {
2037             if (TryToFindMarkedObject(pObj)==SAL_MAX_SIZE) {
2038                 pFndObj=pObj;
2039             } else {
2040                 // TODO: for performance reasons set on to Top or Btm, if necessary
2041             }
2042         }
2043         if (bPrev) no++;
2044     }
2045     if (pFndObj!=nullptr)
2046     {
2047         GetMarkedObjectListWriteAccess().DeleteMark(bPrev?nBtmMarkHit:nTopMarkHit);
2048         GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pFndObj,pPV));
2049         MarkListHasChanged();
2050         AdjustMarkHdl();
2051     }
2052     return pFndObj!=nullptr;
2053 }
2054 
MarkObj(const tools::Rectangle & rRect,bool bUnmark)2055 void SdrMarkView::MarkObj(const tools::Rectangle& rRect, bool bUnmark)
2056 {
2057     bool bFnd=false;
2058     tools::Rectangle aR(rRect);
2059     SdrObjList* pObjList;
2060     BrkAction();
2061     SdrPageView* pPV = GetSdrPageView();
2062 
2063     if(pPV)
2064     {
2065         pObjList=pPV->GetObjList();
2066         tools::Rectangle aFrm1(aR);
2067         const size_t nObjCount = pObjList->GetObjCount();
2068         for (size_t nO=0; nO<nObjCount; ++nO) {
2069             SdrObject* pObj=pObjList->GetObj(nO);
2070             tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2071             if (aFrm1.IsInside(aRect)) {
2072                 if (!bUnmark) {
2073                     if (IsObjMarkable(pObj,pPV))
2074                     {
2075                         GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
2076                         bFnd=true;
2077                     }
2078                 } else {
2079                     const size_t nPos=TryToFindMarkedObject(pObj);
2080                     if (nPos!=SAL_MAX_SIZE)
2081                     {
2082                         GetMarkedObjectListWriteAccess().DeleteMark(nPos);
2083                         bFnd=true;
2084                     }
2085                 }
2086             }
2087         }
2088     }
2089     if (bFnd) {
2090         SortMarkedObjects();
2091         MarkListHasChanged();
2092         AdjustMarkHdl();
2093     }
2094 }
2095 
2096 namespace {
2097 
collectUIInformation(const SdrObject * pObj)2098 void collectUIInformation(const SdrObject* pObj)
2099 {
2100     EventDescription aDescription;
2101     aDescription.aAction = "SELECT";
2102     aDescription.aParent = "MainWindow";
2103     aDescription.aKeyWord = "CurrentApp";
2104 
2105     if (!pObj->GetName().isEmpty())
2106         aDescription.aParameters = {{"OBJECT", pObj->GetName()}};
2107     else
2108         aDescription.aParameters = {{"OBJECT", "Unnamed_Obj_" + OUString::number(pObj->GetOrdNum())}};
2109 
2110     UITestLogger::getInstance().logEvent(aDescription);
2111 }
2112 
2113 }
2114 
MarkObj(SdrObject * pObj,SdrPageView * pPV,bool bUnmark,bool bDoNoSetMarkHdl,std::vector<basegfx::B2DRectangle> const & rSubSelections)2115  void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bDoNoSetMarkHdl,
2116                           std::vector<basegfx::B2DRectangle> const & rSubSelections)
2117 {
2118     if (!(pObj!=nullptr && pPV!=nullptr && IsObjMarkable(pObj, pPV)))
2119         return;
2120 
2121     BrkAction();
2122     if (!bUnmark)
2123     {
2124         GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
2125         collectUIInformation(pObj);
2126     }
2127     else
2128     {
2129         const size_t nPos=TryToFindMarkedObject(pObj);
2130         if (nPos!=SAL_MAX_SIZE)
2131         {
2132             GetMarkedObjectListWriteAccess().DeleteMark(nPos);
2133         }
2134     }
2135 
2136     maSubSelectionList = rSubSelections;
2137 
2138     if (!bDoNoSetMarkHdl) {
2139         MarkListHasChanged();
2140         AdjustMarkHdl();
2141     }
2142 }
2143 
IsObjMarked(SdrObject const * pObj) const2144 bool SdrMarkView::IsObjMarked(SdrObject const * pObj) const
2145 {
2146     return TryToFindMarkedObject(pObj)!=SAL_MAX_SIZE;
2147 }
2148 
GetMarkHdlSizePixel() const2149 sal_uInt16 SdrMarkView::GetMarkHdlSizePixel() const
2150 {
2151     return maHdlList.GetHdlSize()*2+1;
2152 }
2153 
SetMarkHdlSizePixel(sal_uInt16 nSiz)2154 void SdrMarkView::SetMarkHdlSizePixel(sal_uInt16 nSiz)
2155 {
2156     if (nSiz<3) nSiz=3;
2157     nSiz/=2;
2158     if (nSiz!=maHdlList.GetHdlSize()) {
2159         maHdlList.SetHdlSize(nSiz);
2160     }
2161 }
2162 
getPossibleGridOffsetForSdrObject(basegfx::B2DVector & rOffset,const SdrObject * pObj,const SdrPageView * pPV) const2163 bool SdrMarkView::getPossibleGridOffsetForSdrObject(
2164     basegfx::B2DVector& rOffset,
2165     const SdrObject* pObj,
2166     const SdrPageView* pPV) const
2167 {
2168     if(nullptr == pObj || nullptr == pPV)
2169     {
2170         return false;
2171     }
2172 
2173     const OutputDevice* pOutputDevice(GetFirstOutputDevice());
2174 
2175     if(nullptr == pOutputDevice)
2176     {
2177         return false;
2178     }
2179 
2180     const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
2181 
2182     if(nullptr == pSdrPageWindow)
2183     {
2184         return false;
2185     }
2186 
2187     const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
2188 
2189     if(!rObjectContact.supportsGridOffsets())
2190     {
2191         return false;
2192     }
2193 
2194     const sdr::contact::ViewObjectContact& rVOC(pObj->GetViewContact().GetViewObjectContact(
2195         const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
2196 
2197     rOffset = rVOC.getGridOffset();
2198 
2199     return !rOffset.equalZero();
2200 }
2201 
getPossibleGridOffsetForPosition(basegfx::B2DVector & rOffset,const basegfx::B2DPoint & rPoint,const SdrPageView * pPV) const2202 bool SdrMarkView::getPossibleGridOffsetForPosition(
2203     basegfx::B2DVector& rOffset,
2204     const basegfx::B2DPoint& rPoint,
2205     const SdrPageView* pPV) const
2206 {
2207     if(nullptr == pPV)
2208     {
2209         return false;
2210     }
2211 
2212     const OutputDevice* pOutputDevice(GetFirstOutputDevice());
2213 
2214     if(nullptr == pOutputDevice)
2215     {
2216         return false;
2217     }
2218 
2219     const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
2220 
2221     if(nullptr == pSdrPageWindow)
2222     {
2223         return false;
2224     }
2225 
2226     const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
2227 
2228     if(!rObjectContact.supportsGridOffsets())
2229     {
2230         return false;
2231     }
2232 
2233     rObjectContact.calculateGridOffsetForB2DRange(rOffset, basegfx::B2DRange(rPoint));
2234 
2235     return !rOffset.equalZero();
2236 }
2237 
CheckSingleSdrObjectHit(const Point & rPnt,sal_uInt16 nTol,SdrObject * pObj,SdrPageView * pPV,SdrSearchOptions nOptions,const SdrLayerIDSet * pMVisLay) const2238 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const
2239 {
2240     if(((nOptions & SdrSearchOptions::IMPISMASTER) && pObj->IsNotVisibleAsMaster()) || (!pObj->IsVisible()))
2241     {
2242         return nullptr;
2243     }
2244 
2245     const bool bCheckIfMarkable(nOptions & SdrSearchOptions::TESTMARKABLE);
2246     const bool bDeep(nOptions & SdrSearchOptions::DEEP);
2247     const bool bOLE(dynamic_cast< const SdrOle2Obj* >(pObj) !=  nullptr);
2248     auto pTextObj = dynamic_cast<const SdrTextObj*>( pObj);
2249     const bool bTXT(pTextObj && pTextObj->IsTextFrame());
2250     SdrObject* pRet=nullptr;
2251     tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2252 
2253     // add possible GridOffset to up-to-now view-independent BoundRect data
2254     basegfx::B2DVector aGridOffset(0.0, 0.0);
2255     if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, pPV))
2256     {
2257         aRect += Point(
2258             basegfx::fround(aGridOffset.getX()),
2259             basegfx::fround(aGridOffset.getY()));
2260     }
2261 
2262     sal_uInt16 nTol2(nTol);
2263 
2264     // double tolerance for OLE, text frames and objects in
2265     // active text edit
2266     if(bOLE || bTXT || pObj==static_cast<const SdrObjEditView*>(this)->GetTextEditObject())
2267     {
2268         nTol2*=2;
2269     }
2270 
2271     aRect.AdjustLeft( -nTol2 ); // add 1 tolerance for all objects
2272     aRect.AdjustTop( -nTol2 );
2273     aRect.AdjustRight(nTol2 );
2274     aRect.AdjustBottom(nTol2 );
2275 
2276     if (aRect.IsInside(rPnt))
2277     {
2278         if (!bCheckIfMarkable || IsObjMarkable(pObj,pPV))
2279         {
2280             SdrObjList* pOL=pObj->GetSubList();
2281 
2282             if (pOL!=nullptr && pOL->GetObjCount()!=0)
2283             {
2284                 SdrObject* pTmpObj;
2285                 // adjustment hit point for virtual objects
2286                 Point aPnt( rPnt );
2287 
2288                 if ( auto pVirtObj = dynamic_cast<const SdrVirtObj*>( pObj) )
2289                 {
2290                     Point aOffset = pVirtObj->GetOffset();
2291                     aPnt.Move( -aOffset.X(), -aOffset.Y() );
2292                 }
2293 
2294                 pRet=CheckSingleSdrObjectHit(aPnt,nTol,pOL,pPV,nOptions,pMVisLay,pTmpObj);
2295             }
2296             else
2297             {
2298                 if(!pMVisLay || pMVisLay->IsSet(pObj->GetLayer()))
2299                 {
2300                     pRet = SdrObjectPrimitiveHit(*pObj, rPnt, nTol2, *pPV, &pPV->GetVisibleLayers(), false);
2301                 }
2302             }
2303         }
2304     }
2305 
2306     if (!bDeep && pRet!=nullptr)
2307     {
2308         pRet=pObj;
2309     }
2310 
2311     return pRet;
2312 }
2313 
CheckSingleSdrObjectHit(const Point & rPnt,sal_uInt16 nTol,SdrObjList const * pOL,SdrPageView * pPV,SdrSearchOptions nOptions,const SdrLayerIDSet * pMVisLay,SdrObject * & rpRootObj) const2314 SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj) const
2315 {
2316     return (*this).CheckSingleSdrObjectHit(rPnt,nTol,pOL,pPV,nOptions,pMVisLay,rpRootObj,nullptr);
2317 }
CheckSingleSdrObjectHit(const Point & rPnt,sal_uInt16 nTol,SdrObjList const * pOL,SdrPageView * pPV,SdrSearchOptions nOptions,const SdrLayerIDSet * pMVisLay,SdrObject * & rpRootObj,const SdrMarkList * pMarkList) const2318 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
2319 {
2320     SdrObject* pRet=nullptr;
2321     rpRootObj=nullptr;
2322     if (pOL!=nullptr)
2323     {
2324         const bool bRemap(
2325             nullptr != pOL->getSdrObjectFromSdrObjList()
2326             && nullptr != dynamic_cast< const E3dScene* >(pOL->getSdrObjectFromSdrObjList()));
2327         const E3dScene* pRemapScene(bRemap ? static_cast< E3dScene* >(pOL->getSdrObjectFromSdrObjList()) : nullptr);
2328         const size_t nObjCount(pOL->GetObjCount());
2329         size_t nObjNum(nObjCount);
2330 
2331         while (pRet==nullptr && nObjNum>0)
2332         {
2333             nObjNum--;
2334             SdrObject* pObj;
2335 
2336             if(bRemap)
2337             {
2338                 pObj = pOL->GetObj(pRemapScene->RemapOrdNum(nObjNum));
2339             }
2340             else
2341             {
2342                 pObj = pOL->GetObj(nObjNum);
2343             }
2344             if (nOptions & SdrSearchOptions::BEFOREMARK)
2345             {
2346                 if (pMarkList!=nullptr)
2347                 {
2348                     if ((*pMarkList).FindObject(pObj)!=SAL_MAX_SIZE)
2349                     {
2350                         return nullptr;
2351                     }
2352                 }
2353             }
2354             pRet=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,pMVisLay);
2355             if (pRet!=nullptr) rpRootObj=pObj;
2356         }
2357     }
2358     return pRet;
2359 }
2360 
PickObj(const Point & rPnt,short nTol,SdrPageView * & rpPV,SdrSearchOptions nOptions) const2361 SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
2362 {
2363     return PickObj(rPnt, nTol, rpPV, nOptions, nullptr);
2364 }
2365 
PickObj(const Point & rPnt,short nTol,SdrPageView * & rpPV,SdrSearchOptions nOptions,SdrObject ** ppRootObj,bool * pbHitPassDirect) const2366 SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions, SdrObject** ppRootObj, bool* pbHitPassDirect) const
2367 { // TODO: lacks a Pass2,Pass3
2368     SortMarkedObjects();
2369     if (ppRootObj!=nullptr) *ppRootObj=nullptr;
2370     if (pbHitPassDirect!=nullptr) *pbHitPassDirect=true;
2371     SdrObject* pRet = nullptr;
2372     rpPV=nullptr;
2373     bool bMarked(nOptions & SdrSearchOptions::MARKED);
2374     bool bMasters=!bMarked && bool(nOptions & SdrSearchOptions::ALSOONMASTER);
2375     // nOptions & SdrSearchOptions::NEXT: n.i.
2376     // nOptions & SdrSearchOptions::PASS2BOUND: n.i.
2377     // nOptions & SdrSearchOptions::PASS3NEAREST// n.i.
2378     if (nTol<0) nTol=ImpGetHitTolLogic(nTol,nullptr);
2379     SdrObject* pObj=nullptr;
2380     SdrObject* pHitObj=nullptr;
2381     SdrPageView* pPV=nullptr;
2382     if (static_cast<const SdrObjEditView*>(this)->IsTextEditFrameHit(rPnt)) {
2383         pObj=static_cast<const SdrObjEditView*>(this)->GetTextEditObject();
2384         pHitObj=pObj;
2385         pPV=static_cast<const SdrObjEditView*>(this)->GetTextEditPageView();
2386     }
2387     if (bMarked) {
2388         const size_t nMrkCnt=GetMarkedObjectCount();
2389         size_t nMrkNum=nMrkCnt;
2390         while (pHitObj==nullptr && nMrkNum>0) {
2391             nMrkNum--;
2392             SdrMark* pM=GetSdrMarkByIndex(nMrkNum);
2393             pObj=pM->GetMarkedSdrObj();
2394             pPV=pM->GetPageView();
2395             pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,nullptr);
2396         }
2397     }
2398     else
2399     {
2400         pPV = GetSdrPageView();
2401 
2402         if(pPV)
2403         {
2404             SdrPage* pPage=pPV->GetPage();
2405             sal_uInt16 nPgCount=1;
2406 
2407             if(bMasters && pPage->TRG_HasMasterPage())
2408             {
2409                 nPgCount++;
2410             }
2411             bool bWholePage(nOptions & SdrSearchOptions::WHOLEPAGE);
2412             bool bExtraPassForWholePage=bWholePage && pPage!=pPV->GetObjList();
2413             if (bExtraPassForWholePage) nPgCount++; // First search in AktObjList, then on the entire page
2414             sal_uInt16 nPgNum=nPgCount;
2415             while (pHitObj==nullptr && nPgNum>0) {
2416                 SdrSearchOptions nTmpOptions=nOptions;
2417                 nPgNum--;
2418                 const SdrLayerIDSet* pMVisLay=nullptr;
2419                 SdrObjList* pObjList=nullptr;
2420                 if (pbHitPassDirect!=nullptr) *pbHitPassDirect = true;
2421                 if (nPgNum>=nPgCount-1 || (bExtraPassForWholePage && nPgNum>=nPgCount-2))
2422                 {
2423                     pObjList=pPV->GetObjList();
2424                     if (bExtraPassForWholePage && nPgNum==nPgCount-2) {
2425                         pObjList=pPage;
2426                         if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
2427                     }
2428                 }
2429                 else
2430                 {
2431                     // otherwise MasterPage
2432                     SdrPage& rMasterPage = pPage->TRG_GetMasterPage();
2433                     pMVisLay = &pPage->TRG_GetMasterPageVisibleLayers();
2434                     pObjList = &rMasterPage;
2435 
2436                     if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
2437                     nTmpOptions=nTmpOptions | SdrSearchOptions::IMPISMASTER;
2438                 }
2439                 pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObjList,pPV,nTmpOptions,pMVisLay,pObj,&(GetMarkedObjectList()));
2440             }
2441         }
2442     }
2443     if (pHitObj!=nullptr) {
2444         if (ppRootObj!=nullptr) *ppRootObj=pObj;
2445         if (nOptions & SdrSearchOptions::DEEP) pObj=pHitObj;
2446         if (nOptions & SdrSearchOptions::TESTTEXTEDIT) {
2447             if (!pObj->HasTextEdit() || pPV->GetLockedLayers().IsSet(pObj->GetLayer())) {
2448                 pObj=nullptr;
2449             }
2450         }
2451         if (pObj!=nullptr && (nOptions & SdrSearchOptions::TESTMACRO)) {
2452             SdrObjMacroHitRec aHitRec;
2453             aHitRec.aPos=rPnt;
2454             aHitRec.nTol=nTol;
2455             aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
2456             aHitRec.pPageView=pPV;
2457             if (!pObj->HasMacro() || !pObj->IsMacroHit(aHitRec)) pObj=nullptr;
2458         }
2459         if (pObj!=nullptr) {
2460             pRet=pObj;
2461             rpPV=pPV;
2462         }
2463     }
2464     return pRet;
2465 }
2466 
PickMarkedObj(const Point & rPnt,SdrObject * & rpObj,SdrPageView * & rpPV,SdrSearchOptions nOptions) const2467 bool SdrMarkView::PickMarkedObj(const Point& rPnt, SdrObject*& rpObj, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
2468 {
2469     SortMarkedObjects();
2470     const bool bBoundCheckOn2ndPass(nOptions & SdrSearchOptions::PASS2BOUND);
2471     rpObj=nullptr;
2472     rpPV=nullptr;
2473     const size_t nMarkCount=GetMarkedObjectCount();
2474     for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
2475         --nMarkNum;
2476         SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
2477         SdrPageView* pPV=pM->GetPageView();
2478         SdrObject* pObj=pM->GetMarkedSdrObj();
2479         if (CheckSingleSdrObjectHit(rPnt,mnHitTolLog,pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr)) {
2480             rpObj=pObj;
2481             rpPV=pPV;
2482             return true;
2483         }
2484     }
2485     if (bBoundCheckOn2ndPass) {
2486         for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
2487             --nMarkNum;
2488             SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
2489             SdrPageView* pPV=pM->GetPageView();
2490             SdrObject* pObj=pM->GetMarkedSdrObj();
2491             tools::Rectangle aRect(pObj->GetCurrentBoundRect());
2492             aRect.AdjustLeft( -mnHitTolLog );
2493             aRect.AdjustTop( -mnHitTolLog );
2494             aRect.AdjustRight(mnHitTolLog );
2495             aRect.AdjustBottom(mnHitTolLog );
2496             if (aRect.IsInside(rPnt)) {
2497                 rpObj=pObj;
2498                 rpPV=pPV;
2499                 return true;
2500             }
2501         }
2502     }
2503     return false;
2504 }
2505 
2506 
UnmarkAllObj(SdrPageView const * pPV)2507 void SdrMarkView::UnmarkAllObj(SdrPageView const * pPV)
2508 {
2509     if (GetMarkedObjectCount()==0)
2510         return;
2511 
2512     BrkAction();
2513     if (pPV!=nullptr)
2514     {
2515         GetMarkedObjectListWriteAccess().DeletePageView(*pPV);
2516     }
2517     else
2518     {
2519         GetMarkedObjectListWriteAccess().Clear();
2520     }
2521     mpMarkedObj=nullptr;
2522     mpMarkedPV=nullptr;
2523     MarkListHasChanged();
2524     AdjustMarkHdl();
2525 }
2526 
MarkAllObj(SdrPageView * pPV)2527 void SdrMarkView::MarkAllObj(SdrPageView* pPV)
2528 {
2529     BrkAction();
2530 
2531     if(!pPV)
2532     {
2533         pPV = GetSdrPageView();
2534     }
2535 
2536     // #i69171# pPV may still be NULL if there is no SDrPageView (!), e.g. when inserting
2537     // other files
2538     if(pPV)
2539     {
2540         const bool bMarkChg(GetMarkedObjectListWriteAccess().InsertPageView(*pPV));
2541 
2542         if(bMarkChg)
2543         {
2544             MarkListHasChanged();
2545         }
2546     }
2547 
2548     if(GetMarkedObjectCount())
2549     {
2550         AdjustMarkHdl();
2551     }
2552 }
2553 
AdjustMarkHdl(SfxViewShell * pOtherShell)2554 void SdrMarkView::AdjustMarkHdl(SfxViewShell* pOtherShell)
2555 {
2556     CheckMarked();
2557     SetMarkRects();
2558     SetMarkHandles(pOtherShell);
2559 }
2560 
2561 // BoundRect in model coordinates, no GridOffset added
GetMarkedObjBoundRect() const2562 tools::Rectangle SdrMarkView::GetMarkedObjBoundRect() const
2563 {
2564     tools::Rectangle aRect;
2565     for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
2566         SdrMark* pM=GetSdrMarkByIndex(nm);
2567         SdrObject* pO=pM->GetMarkedSdrObj();
2568         tools::Rectangle aR1(pO->GetCurrentBoundRect());
2569         if (aRect.IsEmpty()) aRect=aR1;
2570         else aRect.Union(aR1);
2571     }
2572     return aRect;
2573 }
2574 
2575 // ObjRect in model coordinates, no GridOffset added
GetMarkedObjRect() const2576 const tools::Rectangle& SdrMarkView::GetMarkedObjRect() const
2577 {
2578     if (mbMarkedObjRectDirty) {
2579         const_cast<SdrMarkView*>(this)->mbMarkedObjRectDirty=false;
2580         tools::Rectangle aRect;
2581         for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
2582             SdrMark* pM=GetSdrMarkByIndex(nm);
2583             SdrObject* pO = pM->GetMarkedSdrObj();
2584             if (!pO)
2585                 continue;
2586             tools::Rectangle aR1(pO->GetSnapRect());
2587             if (aRect.IsEmpty()) aRect=aR1;
2588             else aRect.Union(aR1);
2589         }
2590         const_cast<SdrMarkView*>(this)->maMarkedObjRect=aRect;
2591     }
2592     return maMarkedObjRect;
2593 }
2594 
2595 
ImpGetDescriptionString(const char * pStrCacheID,ImpGetDescriptionOptions nOpt) const2596 OUString SdrMarkView::ImpGetDescriptionString(const char* pStrCacheID, ImpGetDescriptionOptions nOpt) const
2597 {
2598     OUString sStr = SvxResId(pStrCacheID);
2599     const sal_Int32 nPos = sStr.indexOf("%1");
2600 
2601     if(nPos != -1)
2602     {
2603         if(nOpt == ImpGetDescriptionOptions::POINTS)
2604         {
2605             sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedPoints());
2606         }
2607         else if(nOpt == ImpGetDescriptionOptions::GLUEPOINTS)
2608         {
2609             sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedGluePoints());
2610         }
2611         else
2612         {
2613             sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedObjects());
2614         }
2615     }
2616 
2617     return sStr.replaceFirst("%2", "0");
2618 }
2619 
2620 
EnterMarkedGroup()2621 void SdrMarkView::EnterMarkedGroup()
2622 {
2623     // We enter only the first group found (in only one PageView), because
2624     // PageView::EnterGroup calls an AdjustMarkHdl.
2625     // TODO: I'll have to prevent that via a flag.
2626     SdrPageView* pPV = GetSdrPageView();
2627 
2628     if(!pPV)
2629         return;
2630 
2631     bool bEnter=false;
2632     for (size_t nm = GetMarkedObjectCount(); nm > 0 && !bEnter;)
2633     {
2634         --nm;
2635         SdrMark* pM=GetSdrMarkByIndex(nm);
2636         if (pM->GetPageView()==pPV) {
2637             SdrObject* pObj=pM->GetMarkedSdrObj();
2638             if (pObj->IsGroupObject()) {
2639                 if (pPV->EnterGroup(pObj)) {
2640                     bEnter=true;
2641                 }
2642             }
2643         }
2644     }
2645 }
2646 
2647 
MarkListHasChanged()2648 void SdrMarkView::MarkListHasChanged()
2649 {
2650     GetMarkedObjectListWriteAccess().SetNameDirty();
2651     maSdrViewSelection.SetEdgesOfMarkedNodesDirty();
2652 
2653     mbMarkedObjRectDirty=true;
2654     mbMarkedPointsRectsDirty=true;
2655     bool bOneEdgeMarked=false;
2656     if (GetMarkedObjectCount()==1) {
2657         const SdrObject* pObj=GetMarkedObjectByIndex(0);
2658         if (pObj->GetObjInventor()==SdrInventor::Default) {
2659             sal_uInt16 nIdent=pObj->GetObjIdentifier();
2660             bOneEdgeMarked=nIdent==OBJ_EDGE;
2661         }
2662     }
2663     ImpSetGlueVisible4(bOneEdgeMarked);
2664 }
2665 
2666 
SetMoveOutside(bool bOn)2667 void SdrMarkView::SetMoveOutside(bool bOn)
2668 {
2669     maHdlList.SetMoveOutside(bOn);
2670 }
2671 
SetDesignMode(bool bOn)2672 void SdrMarkView::SetDesignMode( bool bOn )
2673 {
2674     if ( mbDesignMode != bOn )
2675     {
2676         mbDesignMode = bOn;
2677         SdrPageView* pPageView = GetSdrPageView();
2678         if ( pPageView )
2679             pPageView->SetDesignMode( bOn );
2680     }
2681 }
2682 
2683 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2684