/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include void SdrMark::setTime() { TimeValue aNow; osl_getSystemTime(&aNow); mnTimeStamp = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec; } SdrMark::SdrMark(SdrObject* pNewObj, SdrPageView* pNewPageView) : mpSelectedSdrObject(pNewObj), mpPageView(pNewPageView), mbCon1(false), mbCon2(false), mnUser(0) { if(mpSelectedSdrObject) { mpSelectedSdrObject->AddObjectUser( *this ); } setTime(); } SdrMark::SdrMark(const SdrMark& rMark) : ObjectUser(), mnTimeStamp(0), mpSelectedSdrObject(nullptr), mpPageView(nullptr), mbCon1(false), mbCon2(false), mnUser(0) { *this = rMark; } SdrMark::~SdrMark() { if (mpSelectedSdrObject) { mpSelectedSdrObject->RemoveObjectUser( *this ); } } void SdrMark::ObjectInDestruction(const SdrObject& rObject) { (void) rObject; // avoid warnings OSL_ENSURE(mpSelectedSdrObject && mpSelectedSdrObject == &rObject, "SdrMark::ObjectInDestruction: called from object different from hosted one (!)"); OSL_ENSURE(mpSelectedSdrObject, "SdrMark::ObjectInDestruction: still selected SdrObject is deleted, deselect first (!)"); mpSelectedSdrObject = nullptr; } void SdrMark::SetMarkedSdrObj(SdrObject* pNewObj) { if(mpSelectedSdrObject) { mpSelectedSdrObject->RemoveObjectUser( *this ); } mpSelectedSdrObject = pNewObj; if(mpSelectedSdrObject) { mpSelectedSdrObject->AddObjectUser( *this ); } } SdrMark& SdrMark::operator=(const SdrMark& rMark) { SetMarkedSdrObj(rMark.mpSelectedSdrObject); mnTimeStamp = rMark.mnTimeStamp; mpPageView = rMark.mpPageView; mbCon1 = rMark.mbCon1; mbCon2 = rMark.mbCon2; mnUser = rMark.mnUser; maPoints = rMark.maPoints; maGluePoints = rMark.maGluePoints; return *this; } static bool ImpSdrMarkListSorter(std::unique_ptr const& lhs, std::unique_ptr const& rhs) { SdrObject* pObj1 = lhs->GetMarkedSdrObj(); SdrObject* pObj2 = rhs->GetMarkedSdrObj(); SdrObjList* pOL1 = pObj1 ? pObj1->getParentSdrObjListFromSdrObject() : nullptr; SdrObjList* pOL2 = pObj2 ? pObj2->getParentSdrObjListFromSdrObject() : nullptr; if (pOL1 == pOL2) { // AF: Note that I reverted a change from sal_uInt32 to sal_uLong (made // for 64bit compliance, #i78198#) because internally in SdrObject // both nOrdNum and mnNavigationPosition are stored as sal_uInt32. sal_uInt32 nObjOrd1(pObj1 ? pObj1->GetNavigationPosition() : 0); sal_uInt32 nObjOrd2(pObj2 ? pObj2->GetNavigationPosition() : 0); return nObjOrd1 < nObjOrd2; } else { return pOL1 < pOL2; } } void SdrMarkList::ForceSort() const { if(!mbSorted) { const_cast(this)->ImpForceSort(); } } void SdrMarkList::ImpForceSort() { if(mbSorted) return; mbSorted = true; size_t nCount = maList.size(); // remove invalid if(nCount > 0 ) { std::erase_if(maList, [](std::unique_ptr& rItem) { return rItem->GetMarkedSdrObj() == nullptr; }); nCount = maList.size(); } if(nCount <= 1) return; std::sort(maList.begin(), maList.end(), ImpSdrMarkListSorter); // remove duplicates if(maList.size() <= 1) return; SdrMark* pCurrent = maList.back().get(); for (size_t count = maList.size() - 1; count; --count) { size_t i = count - 1; SdrMark* pCmp = maList[i].get(); assert(pCurrent->GetMarkedSdrObj()); if(pCurrent->GetMarkedSdrObj() == pCmp->GetMarkedSdrObj()) { // Con1/Con2 Merging if(pCmp->IsCon1()) pCurrent->SetCon1(true); if(pCmp->IsCon2()) pCurrent->SetCon2(true); // delete pCmp maList.erase(maList.begin() + i); } else { pCurrent = pCmp; } } } void SdrMarkList::Clear() { maList.clear(); mbSorted = true; //we're empty, so can be considered sorted SetNameDirty(); } SdrMarkList& SdrMarkList::operator=(const SdrMarkList& rLst) { if (this != &rLst) { Clear(); for(size_t i = 0; i < rLst.GetMarkCount(); ++i) { SdrMark* pMark = rLst.GetMark(i); maList.emplace_back(new SdrMark(*pMark)); } maMarkName = rLst.maMarkName; mbNameOk = rLst.mbNameOk; maPointName = rLst.maPointName; mbPointNameOk = rLst.mbPointNameOk; maGluePointName = rLst.maGluePointName; mbSorted = rLst.mbSorted; } return *this; } SdrMark* SdrMarkList::GetMark(size_t nNum) const { return (nNum < maList.size()) ? maList[nNum].get() : nullptr; } size_t SdrMarkList::FindObject(const SdrObject* pObj) const { // Since relying on OrdNums is not allowed for the selection because objects in the // selection may not be inserted in a list if they are e.g. modified ATM, i changed // this loop to just look if the object pointer is in the selection. // Problem is that GetOrdNum() which is const, internally casts to non-const and // hardly sets the OrdNum member of the object (nOrdNum) to 0 (ZERO) if the object // is not inserted in an object list. // Since this may be by purpose and necessary somewhere else i decided that it is // less dangerous to change this method then changing SdrObject::GetOrdNum(). if(pObj) { for(size_t a = 0; a < maList.size(); ++a) { if(maList[a]->GetMarkedSdrObj() == pObj) { return a; } } } return SAL_MAX_SIZE; } void SdrMarkList::InsertEntry(const SdrMark& rMark, bool bChkSort) { SetNameDirty(); const size_t nCount(maList.size()); if(!bChkSort || !mbSorted || nCount == 0) { if(!bChkSort) mbSorted = false; maList.emplace_back(new SdrMark(rMark)); } else { SdrMark* pLast = GetMark(nCount - 1); const SdrObject* pLastObj = pLast->GetMarkedSdrObj(); const SdrObject* pNewObj = rMark.GetMarkedSdrObj(); if(pLastObj == pNewObj) { // This one already exists. // Con1/Con2 Merging if(rMark.IsCon1()) pLast->SetCon1(true); if(rMark.IsCon2()) pLast->SetCon2(true); } else { maList.emplace_back(new SdrMark(rMark)); // now check if the sort is ok const SdrObjList* pLastOL = pLastObj!=nullptr ? pLastObj->getParentSdrObjListFromSdrObject() : nullptr; const SdrObjList* pNewOL = pNewObj !=nullptr ? pNewObj->getParentSdrObjListFromSdrObject() : nullptr; if(pLastOL == pNewOL) { const sal_uInt32 nLastNum(pLastObj!=nullptr ? pLastObj->GetOrdNum() : 0); const sal_uInt32 nNewNum(pNewObj !=nullptr ? pNewObj ->GetOrdNum() : 0); if(nNewNum < nLastNum) { // at some point, we have to sort mbSorted = false; } } else { // at some point, we have to sort mbSorted = false; } } } } void SdrMarkList::DeleteMark(size_t nNum) { SdrMark* pMark = GetMark(nNum); DBG_ASSERT(pMark!=nullptr,"DeleteMark: MarkEntry not found."); if(pMark) { maList.erase(maList.begin() + nNum); if (maList.empty()) mbSorted = true; //we're empty, so can be considered sorted SetNameDirty(); } } void SdrMarkList::ReplaceMark(const SdrMark& rNewMark, size_t nNum) { SdrMark* pMark = GetMark(nNum); DBG_ASSERT(pMark!=nullptr,"ReplaceMark: MarkEntry not found."); if(pMark) { SetNameDirty(); maList[nNum].reset(new SdrMark(rNewMark)); mbSorted = false; } } void SdrMarkList::Merge(const SdrMarkList& rSrcList, bool bReverse) { const size_t nCount(rSrcList.maList.size()); if(rSrcList.mbSorted) { // merge without forcing a Sort in rSrcList bReverse = false; } if(!bReverse) { for(size_t i = 0; i < nCount; ++i) { SdrMark* pM = rSrcList.maList[i].get(); InsertEntry(*pM); } } else { for(size_t i = nCount; i > 0;) { --i; SdrMark* pM = rSrcList.maList[i].get(); InsertEntry(*pM); } } } bool SdrMarkList::DeletePageView(const SdrPageView& rPV) { bool bChgd(false); for(auto it = maList.begin(); it != maList.end(); ) { SdrMark* pMark = it->get(); if(pMark->GetPageView()==&rPV) { it = maList.erase(it); SetNameDirty(); bChgd = true; } else ++it; } return bChgd; } bool SdrMarkList::InsertPageView(const SdrPageView& rPV) { bool bChgd(false); DeletePageView(rPV); // delete all of them, then append the entire page const SdrObjList* pOL = rPV.GetObjList(); for (const rtl::Reference& pObj : *pOL) { bool bDoIt(rPV.IsObjMarkable(pObj.get())); if(bDoIt) { maList.emplace_back(new SdrMark(pObj.get(), const_cast(&rPV))); SetNameDirty(); bChgd = true; } } return bChgd; } const OUString& SdrMarkList::GetMarkDescription() const { const size_t nCount(GetMarkCount()); if(mbNameOk && 1 == nCount) { // if it's a single selection, cache only text frame const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj(); const SdrTextObj* pTextObj = DynCastSdrTextObj( pObj ); if(!pTextObj || !pTextObj->IsTextFrame()) { const_cast(this)->mbNameOk = false; } } if(!mbNameOk) { SdrMark* pMark = GetMark(0); OUString aNam; if(!nCount) { const_cast(this)->maMarkName = SvxResId(STR_ObjNameNoObj); } else if(1 == nCount) { if(pMark->GetMarkedSdrObj()) { aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul(); } } else { if(pMark->GetMarkedSdrObj()) { aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural(); bool bEq(true); for(size_t i = 1; i < GetMarkCount() && bEq; ++i) { SdrMark* pMark2 = GetMark(i); OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural()); bEq = aNam == aStr1; } if(!bEq) { aNam = SvxResId(STR_ObjNamePlural); } } aNam = OUString::number( nCount ) + " " + aNam; } const_cast(this)->maMarkName = aNam; const_cast(this)->mbNameOk = true; } return maMarkName; } const OUString& SdrMarkList::GetPointMarkDescription(bool bGlue) const { bool& rNameOk = const_cast(bGlue ? mbGluePointNameOk : mbPointNameOk); OUString& rName = const_cast(bGlue ? maGluePointName : maPointName); const size_t nMarkCount(GetMarkCount()); size_t nMarkPtCnt(0); size_t nMarkPtObjCnt(0); size_t n1stMarkNum(SAL_MAX_SIZE); for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum) { const SdrMark* pMark = GetMark(nMarkNum); const SdrUShortCont& rPts = bGlue ? pMark->GetMarkedGluePoints() : pMark->GetMarkedPoints(); if (!rPts.empty()) { if(n1stMarkNum == SAL_MAX_SIZE) { n1stMarkNum = nMarkNum; } nMarkPtCnt += rPts.size(); nMarkPtObjCnt++; } if(nMarkPtObjCnt > 1 && rNameOk) { // preliminary decision return rName; } } if(rNameOk && 1 == nMarkPtObjCnt) { // if it's a single selection, cache only text frame const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj(); const SdrTextObj* pTextObj = DynCastSdrTextObj( pObj ); if(!pTextObj || !pTextObj->IsTextFrame()) { rNameOk = false; } } if(!nMarkPtObjCnt) { rName.clear(); rNameOk = true; } else if(!rNameOk) { const SdrMark* pMark = GetMark(n1stMarkNum); OUString aNam; if(1 == nMarkPtObjCnt) { if(pMark->GetMarkedSdrObj()) { aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul(); } } else { if(pMark->GetMarkedSdrObj()) { aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural(); } bool bEq(true); for(size_t i = n1stMarkNum + 1; i < GetMarkCount() && bEq; ++i) { const SdrMark* pMark2 = GetMark(i); const SdrUShortCont& rPts = bGlue ? pMark2->GetMarkedGluePoints() : pMark2->GetMarkedPoints(); if (!rPts.empty() && pMark2->GetMarkedSdrObj()) { OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural()); bEq = aNam == aStr1; } } if(!bEq) { aNam = SvxResId(STR_ObjNamePlural); } aNam = OUString::number( nMarkPtObjCnt ) + " " + aNam; } OUString aStr1; if(1 == nMarkPtCnt) { aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoint : STR_ViewMarkedPoint); } else { aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoints : STR_ViewMarkedPoints); aStr1 = aStr1.replaceFirst("%2", OUString::number( nMarkPtCnt )); } aStr1 = aStr1.replaceFirst("%1", aNam); rName = aStr1; rNameOk = true; } return rName; } bool SdrMarkList::TakeBoundRect(SdrPageView const * pPV, tools::Rectangle& rRect) const { bool bFnd(false); tools::Rectangle aR; for(size_t i = 0; i < GetMarkCount(); ++i) { SdrMark* pMark = GetMark(i); if(!pPV || pMark->GetPageView() == pPV) { if(pMark->GetMarkedSdrObj()) { aR = pMark->GetMarkedSdrObj()->GetCurrentBoundRect(); if(bFnd) { rRect.Union(aR); } else { rRect = aR; bFnd = true; } } } } return bFnd; } bool SdrMarkList::TakeSnapRect(SdrPageView const * pPV, tools::Rectangle& rRect) const { bool bFnd(false); for(size_t i = 0; i < GetMarkCount(); ++i) { SdrMark* pMark = GetMark(i); if(!pPV || pMark->GetPageView() == pPV) { if(pMark->GetMarkedSdrObj()) { tools::Rectangle aR(pMark->GetMarkedSdrObj()->GetSnapRect()); if(bFnd) { rRect.Union(aR); } else { rRect = aR; bFnd = true; } } } } return bFnd; } namespace sdr { ViewSelection::ViewSelection() : mbEdgesOfMarkedNodesDirty(false) { } void ViewSelection::SetEdgesOfMarkedNodesDirty() { if(!mbEdgesOfMarkedNodesDirty) { mbEdgesOfMarkedNodesDirty = true; maEdgesOfMarkedNodes.Clear(); maMarkedEdgesOfMarkedNodes.Clear(); maAllMarkedObjects.clear(); } } const SdrMarkList& ViewSelection::GetEdgesOfMarkedNodes() const { if(mbEdgesOfMarkedNodesDirty) { const_cast(this)->ImpForceEdgesOfMarkedNodes(); } return maEdgesOfMarkedNodes; } const SdrMarkList& ViewSelection::GetMarkedEdgesOfMarkedNodes() const { if(mbEdgesOfMarkedNodesDirty) { const_cast(this)->ImpForceEdgesOfMarkedNodes(); } return maMarkedEdgesOfMarkedNodes; } const std::vector& ViewSelection::GetAllMarkedObjects() const { if(mbEdgesOfMarkedNodesDirty) const_cast(this)->ImpForceEdgesOfMarkedNodes(); return maAllMarkedObjects; } void ViewSelection::ImplCollectCompleteSelection(SdrObject* pObj) { if(!pObj) return; bool bIsGroup(pObj->IsGroupObject()); if(bIsGroup && DynCastE3dObject(pObj) != nullptr && DynCastE3dScene(pObj) == nullptr) { bIsGroup = false; } if(bIsGroup) { SdrObjList* pList = pObj->GetSubList(); for (const rtl::Reference& pObj2 : *pList) ImplCollectCompleteSelection(pObj2.get()); } maAllMarkedObjects.push_back(pObj); } void ViewSelection::ImpForceEdgesOfMarkedNodes() { if(!mbEdgesOfMarkedNodesDirty) return; mbEdgesOfMarkedNodesDirty = false; maMarkedObjectList.ForceSort(); maEdgesOfMarkedNodes.Clear(); maMarkedEdgesOfMarkedNodes.Clear(); maAllMarkedObjects.clear(); // GetMarkCount after ForceSort const size_t nMarkCount(maMarkedObjectList.GetMarkCount()); for(size_t a = 0; a < nMarkCount; ++a) { SdrObject* pCandidate = maMarkedObjectList.GetMark(a)->GetMarkedSdrObj(); if(!pCandidate) continue; // build transitive hull ImplCollectCompleteSelection(pCandidate); // travel over broadcaster/listener to access edges connected to the selected object const SfxBroadcaster* pBC = pCandidate->GetBroadcaster(); if(!pBC) continue; pBC->ForAllListeners( [this, &pCandidate, &a] (SfxListener* pLst) { SdrEdgeObj* pEdge = dynamic_cast( pLst ); if(pEdge && pEdge->IsInserted() && pEdge->getSdrPageFromSdrObject() == pCandidate->getSdrPageFromSdrObject()) { SdrMark aM(pEdge, maMarkedObjectList.GetMark(a)->GetPageView()); if(pEdge->GetConnectedNode(true) == pCandidate) { aM.SetCon1(true); } if(pEdge->GetConnectedNode(false) == pCandidate) { aM.SetCon2(true); } if(SAL_MAX_SIZE == maMarkedObjectList.FindObject(pEdge)) { // check if it itself is selected maEdgesOfMarkedNodes.InsertEntry(aM); } else { maMarkedEdgesOfMarkedNodes.InsertEntry(aM); } } return false; }); } maEdgesOfMarkedNodes.ForceSort(); maMarkedEdgesOfMarkedNodes.ForceSort(); } } // end of namespace sdr /* vim:set shiftwidth=4 softtabstop=4 expandtab: */