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