Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / svdraw / svdmark.cxx
blob866de4920c152ee5480b46fec89e98932ac9ba34
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>
36 #include <cassert>
38 void SdrMark::setTime()
40 TimeValue aNow;
41 osl_getSystemTime(&aNow);
42 mnTimeStamp = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec;
45 SdrMark::SdrMark(SdrObject* pNewObj, SdrPageView* pNewPageView)
46 : mpSelectedSdrObject(pNewObj),
47 mpPageView(pNewPageView),
48 mbCon1(false),
49 mbCon2(false),
50 mnUser(0)
52 if(mpSelectedSdrObject)
54 mpSelectedSdrObject->AddObjectUser( *this );
56 setTime();
59 SdrMark::SdrMark(const SdrMark& rMark)
60 : ObjectUser(),
61 mnTimeStamp(0),
62 mpSelectedSdrObject(nullptr),
63 mpPageView(nullptr),
64 mbCon1(false),
65 mbCon2(false),
66 mnUser(0)
68 *this = rMark;
71 SdrMark::~SdrMark()
73 if (mpSelectedSdrObject)
75 mpSelectedSdrObject->RemoveObjectUser( *this );
79 void SdrMark::ObjectInDestruction(const SdrObject& rObject)
81 (void) rObject; // avoid warnings
82 OSL_ENSURE(mpSelectedSdrObject && mpSelectedSdrObject == &rObject, "SdrMark::ObjectInDestruction: called from object different from hosted one (!)");
83 OSL_ENSURE(mpSelectedSdrObject, "SdrMark::ObjectInDestruction: still selected SdrObject is deleted, deselect first (!)");
84 mpSelectedSdrObject = nullptr;
87 void SdrMark::SetMarkedSdrObj(SdrObject* pNewObj)
89 if(mpSelectedSdrObject)
91 mpSelectedSdrObject->RemoveObjectUser( *this );
94 mpSelectedSdrObject = pNewObj;
96 if(mpSelectedSdrObject)
98 mpSelectedSdrObject->AddObjectUser( *this );
102 SdrMark& SdrMark::operator=(const SdrMark& rMark)
104 SetMarkedSdrObj(rMark.mpSelectedSdrObject);
106 mnTimeStamp = rMark.mnTimeStamp;
107 mpPageView = rMark.mpPageView;
108 mbCon1 = rMark.mbCon1;
109 mbCon2 = rMark.mbCon2;
110 mnUser = rMark.mnUser;
111 maPoints = rMark.maPoints;
112 maGluePoints = rMark.maGluePoints;
114 return *this;
117 static bool ImpSdrMarkListSorter(std::unique_ptr<SdrMark> const& lhs, std::unique_ptr<SdrMark> const& rhs)
119 SdrObject* pObj1 = lhs->GetMarkedSdrObj();
120 SdrObject* pObj2 = rhs->GetMarkedSdrObj();
121 SdrObjList* pOL1 = pObj1 ? pObj1->getParentSdrObjListFromSdrObject() : nullptr;
122 SdrObjList* pOL2 = pObj2 ? pObj2->getParentSdrObjListFromSdrObject() : nullptr;
124 if (pOL1 == pOL2)
126 // AF: Note that I reverted a change from sal_uInt32 to sal_uLong (made
127 // for 64bit compliance, #i78198#) because internally in SdrObject
128 // both nOrdNum and mnNavigationPosition are stored as sal_uInt32.
129 sal_uInt32 nObjOrd1(pObj1 ? pObj1->GetNavigationPosition() : 0);
130 sal_uInt32 nObjOrd2(pObj2 ? pObj2->GetNavigationPosition() : 0);
132 return nObjOrd1 < nObjOrd2;
134 else
136 return pOL1 < pOL2;
141 void SdrMarkList::ForceSort() const
143 if(!mbSorted)
145 const_cast<SdrMarkList*>(this)->ImpForceSort();
149 void SdrMarkList::ImpForceSort()
151 if(mbSorted)
152 return;
154 mbSorted = true;
155 size_t nCount = maList.size();
157 // remove invalid
158 if(nCount > 0 )
160 maList.erase(std::remove_if(maList.begin(), maList.end(),
161 [](std::unique_ptr<SdrMark>& rItem) { return rItem->GetMarkedSdrObj() == nullptr; }),
162 maList.end());
163 nCount = maList.size();
166 if(nCount <= 1)
167 return;
169 std::sort(maList.begin(), maList.end(), ImpSdrMarkListSorter);
171 // remove duplicates
172 if(maList.size() <= 1)
173 return;
175 SdrMark* pCurrent = maList.back().get();
176 for (size_t count = maList.size() - 1; count; --count)
178 size_t i = count - 1;
179 SdrMark* pCmp = maList[i].get();
180 assert(pCurrent->GetMarkedSdrObj());
181 if(pCurrent->GetMarkedSdrObj() == pCmp->GetMarkedSdrObj())
183 // Con1/Con2 Merging
184 if(pCmp->IsCon1())
185 pCurrent->SetCon1(true);
187 if(pCmp->IsCon2())
188 pCurrent->SetCon2(true);
190 // delete pCmp
191 maList.erase(maList.begin() + i);
193 else
195 pCurrent = pCmp;
200 void SdrMarkList::Clear()
202 maList.clear();
203 mbSorted = true; //we're empty, so can be considered sorted
204 SetNameDirty();
207 SdrMarkList& SdrMarkList::operator=(const SdrMarkList& rLst)
209 if (this != &rLst)
211 Clear();
213 for(size_t i = 0; i < rLst.GetMarkCount(); ++i)
215 SdrMark* pMark = rLst.GetMark(i);
216 maList.emplace_back(new SdrMark(*pMark));
219 maMarkName = rLst.maMarkName;
220 mbNameOk = rLst.mbNameOk;
221 maPointName = rLst.maPointName;
222 mbPointNameOk = rLst.mbPointNameOk;
223 maGluePointName = rLst.maGluePointName;
224 mbSorted = rLst.mbSorted;
226 return *this;
229 SdrMark* SdrMarkList::GetMark(size_t nNum) const
231 return (nNum < maList.size()) ? maList[nNum].get() : nullptr;
234 size_t SdrMarkList::FindObject(const SdrObject* pObj) const
236 // Since relying on OrdNums is not allowed for the selection because objects in the
237 // selection may not be inserted in a list if they are e.g. modified ATM, i changed
238 // this loop to just look if the object pointer is in the selection.
240 // Problem is that GetOrdNum() which is const, internally casts to non-const and
241 // hardly sets the OrdNum member of the object (nOrdNum) to 0 (ZERO) if the object
242 // is not inserted in an object list.
243 // Since this may be by purpose and necessary somewhere else i decided that it is
244 // less dangerous to change this method then changing SdrObject::GetOrdNum().
245 if(pObj)
247 for(size_t a = 0; a < maList.size(); ++a)
249 if(maList[a]->GetMarkedSdrObj() == pObj)
251 return a;
256 return SAL_MAX_SIZE;
259 void SdrMarkList::InsertEntry(const SdrMark& rMark, bool bChkSort)
261 SetNameDirty();
262 const size_t nCount(maList.size());
264 if(!bChkSort || !mbSorted || nCount == 0)
266 if(!bChkSort)
267 mbSorted = false;
269 maList.emplace_back(new SdrMark(rMark));
271 else
273 SdrMark* pLast = GetMark(nCount - 1);
274 const SdrObject* pLastObj = pLast->GetMarkedSdrObj();
275 const SdrObject* pNewObj = rMark.GetMarkedSdrObj();
277 if(pLastObj == pNewObj)
279 // This one already exists.
280 // Con1/Con2 Merging
281 if(rMark.IsCon1())
282 pLast->SetCon1(true);
284 if(rMark.IsCon2())
285 pLast->SetCon2(true);
287 else
289 maList.emplace_back(new SdrMark(rMark));
291 // now check if the sort is ok
292 const SdrObjList* pLastOL = pLastObj!=nullptr ? pLastObj->getParentSdrObjListFromSdrObject() : nullptr;
293 const SdrObjList* pNewOL = pNewObj !=nullptr ? pNewObj->getParentSdrObjListFromSdrObject() : nullptr;
295 if(pLastOL == pNewOL)
297 const sal_uLong nLastNum(pLastObj!=nullptr ? pLastObj->GetOrdNum() : 0);
298 const sal_uLong nNewNum(pNewObj !=nullptr ? pNewObj ->GetOrdNum() : 0);
300 if(nNewNum < nLastNum)
302 // at some point, we have to sort
303 mbSorted = false;
306 else
308 // at some point, we have to sort
309 mbSorted = false;
315 void SdrMarkList::DeleteMark(size_t nNum)
317 SdrMark* pMark = GetMark(nNum);
318 DBG_ASSERT(pMark!=nullptr,"DeleteMark: MarkEntry not found.");
320 if(pMark)
322 maList.erase(maList.begin() + nNum);
323 if (maList.empty())
324 mbSorted = true; //we're empty, so can be considered sorted
325 SetNameDirty();
329 void SdrMarkList::ReplaceMark(const SdrMark& rNewMark, size_t nNum)
331 SdrMark* pMark = GetMark(nNum);
332 DBG_ASSERT(pMark!=nullptr,"ReplaceMark: MarkEntry not found.");
334 if(pMark)
336 SetNameDirty();
337 maList[nNum].reset(new SdrMark(rNewMark));
338 mbSorted = false;
342 void SdrMarkList::Merge(const SdrMarkList& rSrcList, bool bReverse)
344 const size_t nCount(rSrcList.maList.size());
346 if(rSrcList.mbSorted)
348 // merge without forcing a Sort in rSrcList
349 bReverse = false;
352 if(!bReverse)
354 for(size_t i = 0; i < nCount; ++i)
356 SdrMark* pM = rSrcList.maList[i].get();
357 InsertEntry(*pM);
360 else
362 for(size_t i = nCount; i > 0;)
364 --i;
365 SdrMark* pM = rSrcList.maList[i].get();
366 InsertEntry(*pM);
371 bool SdrMarkList::DeletePageView(const SdrPageView& rPV)
373 bool bChgd(false);
375 for(auto it = maList.begin(); it != maList.end(); )
377 SdrMark* pMark = it->get();
379 if(pMark->GetPageView()==&rPV)
381 it = maList.erase(it);
382 SetNameDirty();
383 bChgd = true;
385 else
386 ++it;
389 return bChgd;
392 bool SdrMarkList::InsertPageView(const SdrPageView& rPV)
394 bool bChgd(false);
395 DeletePageView(rPV); // delete all of them, then append the entire page
396 const SdrObjList* pOL = rPV.GetObjList();
397 const size_t nObjCount(pOL->GetObjCount());
399 for(size_t nO = 0; nO < nObjCount; ++nO)
401 SdrObject* pObj = pOL->GetObj(nO);
402 bool bDoIt(rPV.IsObjMarkable(pObj));
404 if(bDoIt)
406 maList.emplace_back(new SdrMark(pObj, const_cast<SdrPageView*>(&rPV)));
407 SetNameDirty();
408 bChgd = true;
412 return bChgd;
415 const OUString& SdrMarkList::GetMarkDescription() const
417 const size_t nCount(GetMarkCount());
419 if(mbNameOk && 1 == nCount)
421 // if it's a single selection, cache only text frame
422 const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
423 const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>( pObj );
425 if(!pTextObj || !pTextObj->IsTextFrame())
427 const_cast<SdrMarkList*>(this)->mbNameOk = false;
431 if(!mbNameOk)
433 SdrMark* pMark = GetMark(0);
434 OUString aNam;
436 if(!nCount)
438 const_cast<SdrMarkList*>(this)->maMarkName = SvxResId(STR_ObjNameNoObj);
440 else if(1 == nCount)
442 if(pMark->GetMarkedSdrObj())
444 aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
447 else
449 if(pMark->GetMarkedSdrObj())
451 aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
452 bool bEq(true);
454 for(size_t i = 1; i < GetMarkCount() && bEq; ++i)
456 SdrMark* pMark2 = GetMark(i);
457 OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
458 bEq = aNam == aStr1;
461 if(!bEq)
463 aNam = SvxResId(STR_ObjNamePlural);
467 aNam = OUString::number( nCount ) + " " + aNam;
470 const_cast<SdrMarkList*>(this)->maMarkName = aNam;
471 const_cast<SdrMarkList*>(this)->mbNameOk = true;
474 return maMarkName;
477 const OUString& SdrMarkList::GetPointMarkDescription(bool bGlue) const
479 bool& rNameOk = const_cast<bool&>(bGlue ? mbGluePointNameOk : mbPointNameOk);
480 OUString& rName = const_cast<OUString&>(bGlue ? maGluePointName : maPointName);
481 const size_t nMarkCount(GetMarkCount());
482 size_t nMarkPtCnt(0);
483 size_t nMarkPtObjCnt(0);
484 size_t n1stMarkNum(SAL_MAX_SIZE);
486 for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum)
488 const SdrMark* pMark = GetMark(nMarkNum);
489 const SdrUShortCont& rPts = bGlue ? pMark->GetMarkedGluePoints() : pMark->GetMarkedPoints();
491 if (!rPts.empty())
493 if(n1stMarkNum == SAL_MAX_SIZE)
495 n1stMarkNum = nMarkNum;
498 nMarkPtCnt += rPts.size();
499 nMarkPtObjCnt++;
502 if(nMarkPtObjCnt > 1 && rNameOk)
504 // preliminary decision
505 return rName;
509 if(rNameOk && 1 == nMarkPtObjCnt)
511 // if it's a single selection, cache only text frame
512 const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
513 const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>( pObj );
515 if(!pTextObj || !pTextObj->IsTextFrame())
517 rNameOk = false;
521 if(!nMarkPtObjCnt)
523 rName.clear();
524 rNameOk = true;
526 else if(!rNameOk)
528 const SdrMark* pMark = GetMark(n1stMarkNum);
529 OUString aNam;
531 if(1 == nMarkPtObjCnt)
533 if(pMark->GetMarkedSdrObj())
535 aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
538 else
540 if(pMark->GetMarkedSdrObj())
542 aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
545 bool bEq(true);
547 for(size_t i = n1stMarkNum + 1; i < GetMarkCount() && bEq; ++i)
549 const SdrMark* pMark2 = GetMark(i);
550 const SdrUShortCont& rPts = bGlue ? pMark2->GetMarkedGluePoints() : pMark2->GetMarkedPoints();
552 if (!rPts.empty() && pMark2->GetMarkedSdrObj())
554 OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
555 bEq = aNam == aStr1;
559 if(!bEq)
561 aNam = SvxResId(STR_ObjNamePlural);
564 aNam = OUString::number( nMarkPtObjCnt ) + " " + aNam;
567 OUString aStr1;
569 if(1 == nMarkPtCnt)
571 aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoint : STR_ViewMarkedPoint);
573 else
575 aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoints : STR_ViewMarkedPoints);
576 aStr1 = aStr1.replaceFirst("%2", OUString::number( nMarkPtCnt ));
579 aStr1 = aStr1.replaceFirst("%1", aNam);
580 rName = aStr1;
581 rNameOk = true;
584 return rName;
587 bool SdrMarkList::TakeBoundRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
589 bool bFnd(false);
590 tools::Rectangle aR;
592 for(size_t i = 0; i < GetMarkCount(); ++i)
594 SdrMark* pMark = GetMark(i);
596 if(!pPV || pMark->GetPageView() == pPV)
598 if(pMark->GetMarkedSdrObj())
600 aR = pMark->GetMarkedSdrObj()->GetCurrentBoundRect();
602 if(bFnd)
604 rRect.Union(aR);
606 else
608 rRect = aR;
609 bFnd = true;
615 return bFnd;
618 bool SdrMarkList::TakeSnapRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
620 bool bFnd(false);
622 for(size_t i = 0; i < GetMarkCount(); ++i)
624 SdrMark* pMark = GetMark(i);
626 if(!pPV || pMark->GetPageView() == pPV)
628 if(pMark->GetMarkedSdrObj())
630 tools::Rectangle aR(pMark->GetMarkedSdrObj()->GetSnapRect());
632 if(bFnd)
634 rRect.Union(aR);
636 else
638 rRect = aR;
639 bFnd = true;
645 return bFnd;
649 namespace sdr
651 ViewSelection::ViewSelection()
652 : mbEdgesOfMarkedNodesDirty(false)
656 void ViewSelection::SetEdgesOfMarkedNodesDirty()
658 if(!mbEdgesOfMarkedNodesDirty)
660 mbEdgesOfMarkedNodesDirty = true;
661 maEdgesOfMarkedNodes.Clear();
662 maMarkedEdgesOfMarkedNodes.Clear();
663 maAllMarkedObjects.clear();
667 const SdrMarkList& ViewSelection::GetEdgesOfMarkedNodes() const
669 if(mbEdgesOfMarkedNodesDirty)
671 const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
674 return maEdgesOfMarkedNodes;
677 const SdrMarkList& ViewSelection::GetMarkedEdgesOfMarkedNodes() const
679 if(mbEdgesOfMarkedNodesDirty)
681 const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
684 return maMarkedEdgesOfMarkedNodes;
687 const std::vector<SdrObject*>& ViewSelection::GetAllMarkedObjects() const
689 if(mbEdgesOfMarkedNodesDirty)
690 const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
692 return maAllMarkedObjects;
695 void ViewSelection::ImplCollectCompleteSelection(SdrObject* pObj)
697 if(!pObj)
698 return;
700 bool bIsGroup(pObj->IsGroupObject());
702 if(bIsGroup && dynamic_cast< const E3dObject* >(pObj) != nullptr && dynamic_cast< const E3dScene* >(pObj) == nullptr)
704 bIsGroup = false;
707 if(bIsGroup)
709 SdrObjList* pList = pObj->GetSubList();
711 for(size_t a = 0; a < pList->GetObjCount(); ++a)
713 SdrObject* pObj2 = pList->GetObj(a);
714 ImplCollectCompleteSelection(pObj2);
718 maAllMarkedObjects.push_back(pObj);
721 void ViewSelection::ImpForceEdgesOfMarkedNodes()
723 if(!mbEdgesOfMarkedNodesDirty)
724 return;
726 mbEdgesOfMarkedNodesDirty = false;
727 maMarkedObjectList.ForceSort();
728 maEdgesOfMarkedNodes.Clear();
729 maMarkedEdgesOfMarkedNodes.Clear();
730 maAllMarkedObjects.clear();
732 // GetMarkCount after ForceSort
733 const size_t nMarkCount(maMarkedObjectList.GetMarkCount());
735 for(size_t a = 0; a < nMarkCount; ++a)
737 SdrObject* pCandidate = maMarkedObjectList.GetMark(a)->GetMarkedSdrObj();
739 if(pCandidate)
741 // build transitive hull
742 ImplCollectCompleteSelection(pCandidate);
744 // travel over broadcaster/listener to access edges connected to the selected object
745 const SfxBroadcaster* pBC = pCandidate->GetBroadcaster();
747 if(pBC)
749 const size_t nLstCnt(pBC->GetSizeOfVector());
751 for(size_t nl=0; nl < nLstCnt; ++nl)
753 SfxListener* pLst = pBC->GetListener(nl);
754 SdrEdgeObj* pEdge = dynamic_cast<SdrEdgeObj*>( pLst );
756 if(pEdge && pEdge->IsInserted() && pEdge->getSdrPageFromSdrObject() == pCandidate->getSdrPageFromSdrObject())
758 SdrMark aM(pEdge, maMarkedObjectList.GetMark(a)->GetPageView());
760 if(pEdge->GetConnectedNode(true) == pCandidate)
762 aM.SetCon1(true);
765 if(pEdge->GetConnectedNode(false) == pCandidate)
767 aM.SetCon2(true);
770 if(SAL_MAX_SIZE == maMarkedObjectList.FindObject(pEdge))
772 // check if it itself is selected
773 maEdgesOfMarkedNodes.InsertEntry(aM);
775 else
777 maMarkedEdgesOfMarkedNodes.InsertEntry(aM);
785 maEdgesOfMarkedNodes.ForceSort();
786 maMarkedEdgesOfMarkedNodes.ForceSort();
788 } // end of namespace sdr
790 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */