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