Bump version to 24.04.3.4
[LibreOffice.git] / svx / source / svdraw / svdmark.cxx
blob521e56d44b9d03aa89d834a00ec9253d5fa37281
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <osl/time.h>
23 #include <svx/svdmark.hxx>
24 #include <svx/svdobj.hxx>
25 #include <svx/svdpage.hxx>
26 #include <svx/svdpagv.hxx>
27 #include <svx/strings.hrc>
28 #include <svx/dialmgr.hxx>
31 #include <svx/obj3d.hxx>
32 #include <svx/scene3d.hxx>
33 #include <svl/SfxBroadcaster.hxx>
34 #include <svx/svdoedge.hxx>
35 #include <osl/diagnose.h>
37 #include <cassert>
39 void SdrMark::setTime()
41 TimeValue aNow;
42 osl_getSystemTime(&aNow);
43 mnTimeStamp = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec;
46 SdrMark::SdrMark(SdrObject* pNewObj, SdrPageView* pNewPageView)
47 : mpSelectedSdrObject(pNewObj),
48 mpPageView(pNewPageView),
49 mbCon1(false),
50 mbCon2(false),
51 mnUser(0)
53 if(mpSelectedSdrObject)
55 mpSelectedSdrObject->AddObjectUser( *this );
57 setTime();
60 SdrMark::SdrMark(const SdrMark& rMark)
61 : ObjectUser(),
62 mnTimeStamp(0),
63 mpSelectedSdrObject(nullptr),
64 mpPageView(nullptr),
65 mbCon1(false),
66 mbCon2(false),
67 mnUser(0)
69 *this = rMark;
72 SdrMark::~SdrMark()
74 if (mpSelectedSdrObject)
76 mpSelectedSdrObject->RemoveObjectUser( *this );
80 void SdrMark::ObjectInDestruction(const SdrObject& rObject)
82 (void) rObject; // avoid warnings
83 OSL_ENSURE(mpSelectedSdrObject && mpSelectedSdrObject == &rObject, "SdrMark::ObjectInDestruction: called from object different from hosted one (!)");
84 OSL_ENSURE(mpSelectedSdrObject, "SdrMark::ObjectInDestruction: still selected SdrObject is deleted, deselect first (!)");
85 mpSelectedSdrObject = nullptr;
88 void SdrMark::SetMarkedSdrObj(SdrObject* pNewObj)
90 if(mpSelectedSdrObject)
92 mpSelectedSdrObject->RemoveObjectUser( *this );
95 mpSelectedSdrObject = pNewObj;
97 if(mpSelectedSdrObject)
99 mpSelectedSdrObject->AddObjectUser( *this );
103 SdrMark& SdrMark::operator=(const SdrMark& rMark)
105 SetMarkedSdrObj(rMark.mpSelectedSdrObject);
107 mnTimeStamp = rMark.mnTimeStamp;
108 mpPageView = rMark.mpPageView;
109 mbCon1 = rMark.mbCon1;
110 mbCon2 = rMark.mbCon2;
111 mnUser = rMark.mnUser;
112 maPoints = rMark.maPoints;
113 maGluePoints = rMark.maGluePoints;
115 return *this;
118 static bool ImpSdrMarkListSorter(std::unique_ptr<SdrMark> const& lhs, std::unique_ptr<SdrMark> const& rhs)
120 SdrObject* pObj1 = lhs->GetMarkedSdrObj();
121 SdrObject* pObj2 = rhs->GetMarkedSdrObj();
122 SdrObjList* pOL1 = pObj1 ? pObj1->getParentSdrObjListFromSdrObject() : nullptr;
123 SdrObjList* pOL2 = pObj2 ? pObj2->getParentSdrObjListFromSdrObject() : nullptr;
125 if (pOL1 == pOL2)
127 // AF: Note that I reverted a change from sal_uInt32 to sal_uLong (made
128 // for 64bit compliance, #i78198#) because internally in SdrObject
129 // both nOrdNum and mnNavigationPosition are stored as sal_uInt32.
130 sal_uInt32 nObjOrd1(pObj1 ? pObj1->GetNavigationPosition() : 0);
131 sal_uInt32 nObjOrd2(pObj2 ? pObj2->GetNavigationPosition() : 0);
133 return nObjOrd1 < nObjOrd2;
135 else
137 return pOL1 < pOL2;
142 void SdrMarkList::ForceSort() const
144 if(!mbSorted)
146 const_cast<SdrMarkList*>(this)->ImpForceSort();
150 void SdrMarkList::ImpForceSort()
152 if(mbSorted)
153 return;
155 mbSorted = true;
156 size_t nCount = maList.size();
158 // remove invalid
159 if(nCount > 0 )
161 std::erase_if(maList, [](std::unique_ptr<SdrMark>& rItem) { return rItem->GetMarkedSdrObj() == nullptr; });
162 nCount = maList.size();
165 if(nCount <= 1)
166 return;
168 std::sort(maList.begin(), maList.end(), ImpSdrMarkListSorter);
170 // remove duplicates
171 if(maList.size() <= 1)
172 return;
174 SdrMark* pCurrent = maList.back().get();
175 for (size_t count = maList.size() - 1; count; --count)
177 size_t i = count - 1;
178 SdrMark* pCmp = maList[i].get();
179 assert(pCurrent->GetMarkedSdrObj());
180 if(pCurrent->GetMarkedSdrObj() == pCmp->GetMarkedSdrObj())
182 // Con1/Con2 Merging
183 if(pCmp->IsCon1())
184 pCurrent->SetCon1(true);
186 if(pCmp->IsCon2())
187 pCurrent->SetCon2(true);
189 // delete pCmp
190 maList.erase(maList.begin() + i);
192 else
194 pCurrent = pCmp;
199 void SdrMarkList::Clear()
201 maList.clear();
202 mbSorted = true; //we're empty, so can be considered sorted
203 SetNameDirty();
206 SdrMarkList& SdrMarkList::operator=(const SdrMarkList& rLst)
208 if (this != &rLst)
210 Clear();
212 for(size_t i = 0; i < rLst.GetMarkCount(); ++i)
214 SdrMark* pMark = rLst.GetMark(i);
215 maList.emplace_back(new SdrMark(*pMark));
218 maMarkName = rLst.maMarkName;
219 mbNameOk = rLst.mbNameOk;
220 maPointName = rLst.maPointName;
221 mbPointNameOk = rLst.mbPointNameOk;
222 maGluePointName = rLst.maGluePointName;
223 mbSorted = rLst.mbSorted;
225 return *this;
228 SdrMark* SdrMarkList::GetMark(size_t nNum) const
230 return (nNum < maList.size()) ? maList[nNum].get() : nullptr;
233 size_t SdrMarkList::FindObject(const SdrObject* pObj) const
235 // Since relying on OrdNums is not allowed for the selection because objects in the
236 // selection may not be inserted in a list if they are e.g. modified ATM, i changed
237 // this loop to just look if the object pointer is in the selection.
239 // Problem is that GetOrdNum() which is const, internally casts to non-const and
240 // hardly sets the OrdNum member of the object (nOrdNum) to 0 (ZERO) if the object
241 // is not inserted in an object list.
242 // Since this may be by purpose and necessary somewhere else i decided that it is
243 // less dangerous to change this method then changing SdrObject::GetOrdNum().
244 if(pObj)
246 for(size_t a = 0; a < maList.size(); ++a)
248 if(maList[a]->GetMarkedSdrObj() == pObj)
250 return a;
255 return SAL_MAX_SIZE;
258 void SdrMarkList::InsertEntry(const SdrMark& rMark, bool bChkSort)
260 SetNameDirty();
261 const size_t nCount(maList.size());
263 if(!bChkSort || !mbSorted || nCount == 0)
265 if(!bChkSort)
266 mbSorted = false;
268 maList.emplace_back(new SdrMark(rMark));
270 else
272 SdrMark* pLast = GetMark(nCount - 1);
273 const SdrObject* pLastObj = pLast->GetMarkedSdrObj();
274 const SdrObject* pNewObj = rMark.GetMarkedSdrObj();
276 if(pLastObj == pNewObj)
278 // This one already exists.
279 // Con1/Con2 Merging
280 if(rMark.IsCon1())
281 pLast->SetCon1(true);
283 if(rMark.IsCon2())
284 pLast->SetCon2(true);
286 else
288 maList.emplace_back(new SdrMark(rMark));
290 // now check if the sort is ok
291 const SdrObjList* pLastOL = pLastObj!=nullptr ? pLastObj->getParentSdrObjListFromSdrObject() : nullptr;
292 const SdrObjList* pNewOL = pNewObj !=nullptr ? pNewObj->getParentSdrObjListFromSdrObject() : nullptr;
294 if(pLastOL == pNewOL)
296 const sal_uLong nLastNum(pLastObj!=nullptr ? pLastObj->GetOrdNum() : 0);
297 const sal_uLong nNewNum(pNewObj !=nullptr ? pNewObj ->GetOrdNum() : 0);
299 if(nNewNum < nLastNum)
301 // at some point, we have to sort
302 mbSorted = false;
305 else
307 // at some point, we have to sort
308 mbSorted = false;
314 void SdrMarkList::DeleteMark(size_t nNum)
316 SdrMark* pMark = GetMark(nNum);
317 DBG_ASSERT(pMark!=nullptr,"DeleteMark: MarkEntry not found.");
319 if(pMark)
321 maList.erase(maList.begin() + nNum);
322 if (maList.empty())
323 mbSorted = true; //we're empty, so can be considered sorted
324 SetNameDirty();
328 void SdrMarkList::ReplaceMark(const SdrMark& rNewMark, size_t nNum)
330 SdrMark* pMark = GetMark(nNum);
331 DBG_ASSERT(pMark!=nullptr,"ReplaceMark: MarkEntry not found.");
333 if(pMark)
335 SetNameDirty();
336 maList[nNum].reset(new SdrMark(rNewMark));
337 mbSorted = false;
341 void SdrMarkList::Merge(const SdrMarkList& rSrcList, bool bReverse)
343 const size_t nCount(rSrcList.maList.size());
345 if(rSrcList.mbSorted)
347 // merge without forcing a Sort in rSrcList
348 bReverse = false;
351 if(!bReverse)
353 for(size_t i = 0; i < nCount; ++i)
355 SdrMark* pM = rSrcList.maList[i].get();
356 InsertEntry(*pM);
359 else
361 for(size_t i = nCount; i > 0;)
363 --i;
364 SdrMark* pM = rSrcList.maList[i].get();
365 InsertEntry(*pM);
370 bool SdrMarkList::DeletePageView(const SdrPageView& rPV)
372 bool bChgd(false);
374 for(auto it = maList.begin(); it != maList.end(); )
376 SdrMark* pMark = it->get();
378 if(pMark->GetPageView()==&rPV)
380 it = maList.erase(it);
381 SetNameDirty();
382 bChgd = true;
384 else
385 ++it;
388 return bChgd;
391 bool SdrMarkList::InsertPageView(const SdrPageView& rPV)
393 bool bChgd(false);
394 DeletePageView(rPV); // delete all of them, then append the entire page
395 const SdrObjList* pOL = rPV.GetObjList();
397 for (const rtl::Reference<SdrObject>& pObj : *pOL)
399 bool bDoIt(rPV.IsObjMarkable(pObj.get()));
401 if(bDoIt)
403 maList.emplace_back(new SdrMark(pObj.get(), const_cast<SdrPageView*>(&rPV)));
404 SetNameDirty();
405 bChgd = true;
409 return bChgd;
412 const OUString& SdrMarkList::GetMarkDescription() const
414 const size_t nCount(GetMarkCount());
416 if(mbNameOk && 1 == nCount)
418 // if it's a single selection, cache only text frame
419 const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
420 const SdrTextObj* pTextObj = DynCastSdrTextObj( pObj );
422 if(!pTextObj || !pTextObj->IsTextFrame())
424 const_cast<SdrMarkList*>(this)->mbNameOk = false;
428 if(!mbNameOk)
430 SdrMark* pMark = GetMark(0);
431 OUString aNam;
433 if(!nCount)
435 const_cast<SdrMarkList*>(this)->maMarkName = SvxResId(STR_ObjNameNoObj);
437 else if(1 == nCount)
439 if(pMark->GetMarkedSdrObj())
441 aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
444 else
446 if(pMark->GetMarkedSdrObj())
448 aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
449 bool bEq(true);
451 for(size_t i = 1; i < GetMarkCount() && bEq; ++i)
453 SdrMark* pMark2 = GetMark(i);
454 OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
455 bEq = aNam == aStr1;
458 if(!bEq)
460 aNam = SvxResId(STR_ObjNamePlural);
464 aNam = OUString::number( nCount ) + " " + aNam;
467 const_cast<SdrMarkList*>(this)->maMarkName = aNam;
468 const_cast<SdrMarkList*>(this)->mbNameOk = true;
471 return maMarkName;
474 const OUString& SdrMarkList::GetPointMarkDescription(bool bGlue) const
476 bool& rNameOk = const_cast<bool&>(bGlue ? mbGluePointNameOk : mbPointNameOk);
477 OUString& rName = const_cast<OUString&>(bGlue ? maGluePointName : maPointName);
478 const size_t nMarkCount(GetMarkCount());
479 size_t nMarkPtCnt(0);
480 size_t nMarkPtObjCnt(0);
481 size_t n1stMarkNum(SAL_MAX_SIZE);
483 for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum)
485 const SdrMark* pMark = GetMark(nMarkNum);
486 const SdrUShortCont& rPts = bGlue ? pMark->GetMarkedGluePoints() : pMark->GetMarkedPoints();
488 if (!rPts.empty())
490 if(n1stMarkNum == SAL_MAX_SIZE)
492 n1stMarkNum = nMarkNum;
495 nMarkPtCnt += rPts.size();
496 nMarkPtObjCnt++;
499 if(nMarkPtObjCnt > 1 && rNameOk)
501 // preliminary decision
502 return rName;
506 if(rNameOk && 1 == nMarkPtObjCnt)
508 // if it's a single selection, cache only text frame
509 const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
510 const SdrTextObj* pTextObj = DynCastSdrTextObj( pObj );
512 if(!pTextObj || !pTextObj->IsTextFrame())
514 rNameOk = false;
518 if(!nMarkPtObjCnt)
520 rName.clear();
521 rNameOk = true;
523 else if(!rNameOk)
525 const SdrMark* pMark = GetMark(n1stMarkNum);
526 OUString aNam;
528 if(1 == nMarkPtObjCnt)
530 if(pMark->GetMarkedSdrObj())
532 aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
535 else
537 if(pMark->GetMarkedSdrObj())
539 aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
542 bool bEq(true);
544 for(size_t i = n1stMarkNum + 1; i < GetMarkCount() && bEq; ++i)
546 const SdrMark* pMark2 = GetMark(i);
547 const SdrUShortCont& rPts = bGlue ? pMark2->GetMarkedGluePoints() : pMark2->GetMarkedPoints();
549 if (!rPts.empty() && pMark2->GetMarkedSdrObj())
551 OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
552 bEq = aNam == aStr1;
556 if(!bEq)
558 aNam = SvxResId(STR_ObjNamePlural);
561 aNam = OUString::number( nMarkPtObjCnt ) + " " + aNam;
564 OUString aStr1;
566 if(1 == nMarkPtCnt)
568 aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoint : STR_ViewMarkedPoint);
570 else
572 aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoints : STR_ViewMarkedPoints);
573 aStr1 = aStr1.replaceFirst("%2", OUString::number( nMarkPtCnt ));
576 aStr1 = aStr1.replaceFirst("%1", aNam);
577 rName = aStr1;
578 rNameOk = true;
581 return rName;
584 bool SdrMarkList::TakeBoundRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
586 bool bFnd(false);
587 tools::Rectangle aR;
589 for(size_t i = 0; i < GetMarkCount(); ++i)
591 SdrMark* pMark = GetMark(i);
593 if(!pPV || pMark->GetPageView() == pPV)
595 if(pMark->GetMarkedSdrObj())
597 aR = pMark->GetMarkedSdrObj()->GetCurrentBoundRect();
599 if(bFnd)
601 rRect.Union(aR);
603 else
605 rRect = aR;
606 bFnd = true;
612 return bFnd;
615 bool SdrMarkList::TakeSnapRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
617 bool bFnd(false);
619 for(size_t i = 0; i < GetMarkCount(); ++i)
621 SdrMark* pMark = GetMark(i);
623 if(!pPV || pMark->GetPageView() == pPV)
625 if(pMark->GetMarkedSdrObj())
627 tools::Rectangle aR(pMark->GetMarkedSdrObj()->GetSnapRect());
629 if(bFnd)
631 rRect.Union(aR);
633 else
635 rRect = aR;
636 bFnd = true;
642 return bFnd;
646 namespace sdr
648 ViewSelection::ViewSelection()
649 : mbEdgesOfMarkedNodesDirty(false)
653 void ViewSelection::SetEdgesOfMarkedNodesDirty()
655 if(!mbEdgesOfMarkedNodesDirty)
657 mbEdgesOfMarkedNodesDirty = true;
658 maEdgesOfMarkedNodes.Clear();
659 maMarkedEdgesOfMarkedNodes.Clear();
660 maAllMarkedObjects.clear();
664 const SdrMarkList& ViewSelection::GetEdgesOfMarkedNodes() const
666 if(mbEdgesOfMarkedNodesDirty)
668 const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
671 return maEdgesOfMarkedNodes;
674 const SdrMarkList& ViewSelection::GetMarkedEdgesOfMarkedNodes() const
676 if(mbEdgesOfMarkedNodesDirty)
678 const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
681 return maMarkedEdgesOfMarkedNodes;
684 const std::vector<SdrObject*>& ViewSelection::GetAllMarkedObjects() const
686 if(mbEdgesOfMarkedNodesDirty)
687 const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
689 return maAllMarkedObjects;
692 void ViewSelection::ImplCollectCompleteSelection(SdrObject* pObj)
694 if(!pObj)
695 return;
697 bool bIsGroup(pObj->IsGroupObject());
699 if(bIsGroup && DynCastE3dObject(pObj) != nullptr && DynCastE3dScene(pObj) == nullptr)
701 bIsGroup = false;
704 if(bIsGroup)
706 SdrObjList* pList = pObj->GetSubList();
708 for (const rtl::Reference<SdrObject>& pObj2 : *pList)
709 ImplCollectCompleteSelection(pObj2.get());
712 maAllMarkedObjects.push_back(pObj);
715 void ViewSelection::ImpForceEdgesOfMarkedNodes()
717 if(!mbEdgesOfMarkedNodesDirty)
718 return;
720 mbEdgesOfMarkedNodesDirty = false;
721 maMarkedObjectList.ForceSort();
722 maEdgesOfMarkedNodes.Clear();
723 maMarkedEdgesOfMarkedNodes.Clear();
724 maAllMarkedObjects.clear();
726 // GetMarkCount after ForceSort
727 const size_t nMarkCount(maMarkedObjectList.GetMarkCount());
729 for(size_t a = 0; a < nMarkCount; ++a)
731 SdrObject* pCandidate = maMarkedObjectList.GetMark(a)->GetMarkedSdrObj();
732 if(!pCandidate)
733 continue;
735 // build transitive hull
736 ImplCollectCompleteSelection(pCandidate);
738 // travel over broadcaster/listener to access edges connected to the selected object
739 const SfxBroadcaster* pBC = pCandidate->GetBroadcaster();
740 if(!pBC)
741 continue;
743 pBC->ForAllListeners(
744 [this, &pCandidate, &a] (SfxListener* pLst)
746 SdrEdgeObj* pEdge = dynamic_cast<SdrEdgeObj*>( pLst );
748 if(pEdge && pEdge->IsInserted() && pEdge->getSdrPageFromSdrObject() == pCandidate->getSdrPageFromSdrObject())
750 SdrMark aM(pEdge, maMarkedObjectList.GetMark(a)->GetPageView());
752 if(pEdge->GetConnectedNode(true) == pCandidate)
754 aM.SetCon1(true);
757 if(pEdge->GetConnectedNode(false) == pCandidate)
759 aM.SetCon2(true);
762 if(SAL_MAX_SIZE == maMarkedObjectList.FindObject(pEdge))
764 // check if it itself is selected
765 maEdgesOfMarkedNodes.InsertEntry(aM);
767 else
769 maMarkedEdgesOfMarkedNodes.InsertEntry(aM);
772 return false;
775 maEdgesOfMarkedNodes.ForceSort();
776 maMarkedEdgesOfMarkedNodes.ForceSort();
778 } // end of namespace sdr
780 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */