/* -*- 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 "svdfmtf.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include using ::std::vector; using namespace com::sun::star; SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const { return nullptr; } SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const { return nullptr; } void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, size_t /*nOldPos*/, size_t /*nNewPos*/) { } void SdrEditView::MovMarkedToTop() { const size_t nCount=GetMarkedObjectCount(); if (nCount!=0) { const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(ImpGetResStr(STR_EditMovToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToTop); SortMarkedObjects(); for (size_t nm=0; nmGetOrdNum(); } bool bChg=false; SdrObjList* pOL0=nullptr; size_t nNewPos=0; for (size_t nm=nCount; nm>0;) { --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pObj=pM->GetMarkedSdrObj(); SdrObjList* pOL=pObj->GetObjList(); if (pOL!=pOL0) { nNewPos = pOL->GetObjCount()-1; pOL0=pOL; } const size_t nNowPos = pObj->GetOrdNumDirect(); const tools::Rectangle& rBR=pObj->GetCurrentBoundRect(); size_t nCmpPos = nNowPos+1; SdrObject* pMaxObj=GetMaxToTopObj(pObj); if (pMaxObj!=nullptr) { size_t nMaxPos=pMaxObj->GetOrdNum(); if (nMaxPos!=0) nMaxPos--; if (nNewPos>nMaxPos) nNewPos=nMaxPos; // neither go faster... if (nNewPosGetObj(nCmpPos); if (pCmpObj==nullptr) { OSL_FAIL("MovMarkedToTop(): Reference object not found."); bEnd=true; } else if (pCmpObj==pMaxObj) { nNewPos=nCmpPos; nNewPos--; bEnd=true; } else if (rBR.IsOver(pCmpObj->GetCurrentBoundRect())) { nNewPos=nCmpPos; bEnd=true; } else { nCmpPos++; } } if (nNowPos!=nNewPos) { bChg=true; pOL->SetObjectOrdNum(nNowPos,nNewPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos)); ObjOrderChanged(pObj,nNowPos,nNewPos); } nNewPos--; } if( bUndo ) EndUndo(); if (bChg) MarkListHasChanged(); } } void SdrEditView::MovMarkedToBtm() { const size_t nCount=GetMarkedObjectCount(); if (nCount!=0) { const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(ImpGetResStr(STR_EditMovToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToBottom); SortMarkedObjects(); for (size_t nm=0; nmGetOrdNum(); } bool bChg=false; SdrObjList* pOL0=nullptr; size_t nNewPos=0; for (size_t nm=0; nmGetMarkedSdrObj(); SdrObjList* pOL=pObj->GetObjList(); if (pOL!=pOL0) { nNewPos=0; pOL0=pOL; } const size_t nNowPos = pObj->GetOrdNumDirect(); const tools::Rectangle& rBR=pObj->GetCurrentBoundRect(); size_t nCmpPos = nNowPos; if (nCmpPos>0) --nCmpPos; SdrObject* pMaxObj=GetMaxToBtmObj(pObj); if (pMaxObj!=nullptr) { const size_t nMinPos=pMaxObj->GetOrdNum()+1; if (nNewPosnNowPos) nNewPos=nNowPos; // nor go in the other direction } bool bEnd=false; // nNewPos in this case is the "maximum" position // the object may reach without going faster than the object before // it (multiple selection). while (nCmpPos>nNewPos && !bEnd) { SdrObject* pCmpObj=pOL->GetObj(nCmpPos); if (pCmpObj==nullptr) { OSL_FAIL("MovMarkedToBtm(): Reference object not found."); bEnd=true; } else if (pCmpObj==pMaxObj) { nNewPos=nCmpPos; nNewPos++; bEnd=true; } else if (rBR.IsOver(pCmpObj->GetCurrentBoundRect())) { nNewPos=nCmpPos; bEnd=true; } else { nCmpPos--; } } if (nNowPos!=nNewPos) { bChg=true; pOL->SetObjectOrdNum(nNowPos,nNewPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos)); ObjOrderChanged(pObj,nNowPos,nNewPos); } nNewPos++; } if(bUndo) EndUndo(); if(bChg) MarkListHasChanged(); } } void SdrEditView::PutMarkedToTop() { PutMarkedInFrontOfObj(nullptr); } void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj) { const size_t nCount=GetMarkedObjectCount(); if (nCount==0) return; const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(ImpGetResStr(STR_EditPutToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToTop); SortMarkedObjects(); if (pRefObj!=nullptr) { // Make "in front of the object" work, even if the // selected objects are already in front of the other object const size_t nRefMark=TryToFindMarkedObject(pRefObj); SdrMark aRefMark; if (nRefMark!=SAL_MAX_SIZE) { aRefMark=*GetSdrMarkByIndex(nRefMark); GetMarkedObjectListWriteAccess().DeleteMark(nRefMark); } PutMarkedToBtm(); if (nRefMark!=SAL_MAX_SIZE) { GetMarkedObjectListWriteAccess().InsertEntry(aRefMark); SortMarkedObjects(); } } for (size_t nm=0; nmGetOrdNum(); } bool bChg=false; SdrObjList* pOL0=nullptr; size_t nNewPos=0; for (size_t nm=nCount; nm>0;) { --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pObj=pM->GetMarkedSdrObj(); if (pObj!=pRefObj) { SdrObjList* pOL=pObj->GetObjList(); if (pOL!=pOL0) { nNewPos=pOL->GetObjCount()-1; pOL0=pOL; } const size_t nNowPos=pObj->GetOrdNumDirect(); SdrObject* pMaxObj=GetMaxToTopObj(pObj); if (pMaxObj!=nullptr) { size_t nMaxOrd=pMaxObj->GetOrdNum(); // sadly doesn't work any other way if (nMaxOrd>0) nMaxOrd--; if (nNewPos>nMaxOrd) nNewPos=nMaxOrd; // neither go faster... if (nNewPosGetObjList()==pObj->GetObjList()) { const size_t nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way if (nNewPos>nMaxOrd) nNewPos=nMaxOrd; // neither go faster... if (nNewPosSetObjectOrdNum(nNowPos,nNewPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos)); ObjOrderChanged(pObj,nNowPos,nNewPos); } nNewPos--; } // if (pObj!=pRefObj) } // for loop over all selected objects if( bUndo ) EndUndo(); if(bChg) MarkListHasChanged(); } void SdrEditView::PutMarkedToBtm() { PutMarkedBehindObj(nullptr); } void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj) { const size_t nCount=GetMarkedObjectCount(); if (nCount==0) return; const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(ImpGetResStr(STR_EditPutToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToBottom); SortMarkedObjects(); if (pRefObj!=nullptr) { // Make "behind the object" work, even if the // selected objects are already behind the other object const size_t nRefMark=TryToFindMarkedObject(pRefObj); SdrMark aRefMark; if (nRefMark!=SAL_MAX_SIZE) { aRefMark=*GetSdrMarkByIndex(nRefMark); GetMarkedObjectListWriteAccess().DeleteMark(nRefMark); } PutMarkedToTop(); if (nRefMark!=SAL_MAX_SIZE) { GetMarkedObjectListWriteAccess().InsertEntry(aRefMark); SortMarkedObjects(); } } for (size_t nm=0; nmGetOrdNum(); } bool bChg=false; SdrObjList* pOL0=nullptr; size_t nNewPos=0; for (size_t nm=0; nmGetMarkedSdrObj(); if (pObj!=pRefObj) { SdrObjList* pOL=pObj->GetObjList(); if (pOL!=pOL0) { nNewPos=0; pOL0=pOL; } const size_t nNowPos=pObj->GetOrdNumDirect(); SdrObject* pMinObj=GetMaxToBtmObj(pObj); if (pMinObj!=nullptr) { const size_t nMinOrd=pMinObj->GetOrdNum()+1; // sadly doesn't work any differently if (nNewPosnNowPos) nNewPos=nNowPos; // nor go into the other direction } if (pRefObj!=nullptr) { if (pRefObj->GetObjList()==pObj->GetObjList()) { const size_t nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently if (nNewPosnNowPos) nNewPos=nNowPos; // nor go into the other direction } else { nNewPos=nNowPos; // different PageView, so don't change } } if (nNowPos!=nNewPos) { bChg=true; pOL->SetObjectOrdNum(nNowPos,nNewPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos)); ObjOrderChanged(pObj,nNowPos,nNewPos); } nNewPos++; } // if (pObj!=pRefObj) } // for loop over all selected objects if(bUndo) EndUndo(); if(bChg) MarkListHasChanged(); } void SdrEditView::ReverseOrderOfMarked() { SortMarkedObjects(); const size_t nMarkCount=GetMarkedObjectCount(); if (nMarkCount>0) { bool bChg=false; bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(ImpGetResStr(STR_EditRevOrder),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ReverseOrder); size_t a=0; do { // take into account selection across multiple PageViews size_t b=a+1; while (bGetObjList(); size_t c=b; if (aGetOrdNum(); } while (aGetOrdNumDirect(); const size_t nOrd2=pObj2->GetOrdNumDirect(); if( bUndo ) { AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2)); AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1)); } pOL->SetObjectOrdNum(nOrd1,nOrd2); // Obj 2 has moved forward by one position, so now nOrd2-1 pOL->SetObjectOrdNum(nOrd2-1,nOrd1); // use Replace instead of SetOrdNum for performance reasons (recalculation of Ordnums) ++a; --c; bChg=true; } a=b+1; } while (aGetObjList(); SAL_WARN_IF(!pOL, "svx", "Object somehow has no ObjList"); size_t nMax = pOL ? pOL->GetObjCount() : 0; size_t nMin = 0; const size_t nObjNum=pObj->GetOrdNum(); SdrObject* pRestrict=GetMaxToTopObj(pObj); if (pRestrict!=nullptr) { const size_t nRestrict=pRestrict->GetOrdNum(); if (nRestrictGetOrdNum(); if (nRestrict>nMin) nMin=nRestrict; } bToTopPossible=nObjNumnMin; } else { // multiple selection SdrObjList* pOL0=nullptr; size_t nPos0 = 0; for (size_t nm = 0; !bToBtmPossible && nmGetObjList(); if (pOL!=pOL0) { nPos0 = 0; pOL0=pOL; } const size_t nPos = pObj->GetOrdNum(); bToBtmPossible = nPos && (nPos-1 > nPos0); nPos0 = nPos; } pOL0=nullptr; nPos0 = SAL_MAX_SIZE; for (size_t nm=nCount; !bToTopPossible && nm>0; ) { // check 'bring to front' --nm; SdrObject* pObj=GetMarkedObjectByIndex(nm); SdrObjList* pOL=pObj->GetObjList(); if (pOL!=pOL0) { nPos0=pOL->GetObjCount(); pOL0=pOL; } const size_t nPos = pObj->GetOrdNum(); bToTopPossible = nPos+1 < nPos0; nPos0=nPos; } } } // Combine void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const { if (pSource!=nullptr) { SdrObjList* pOL=pSource->GetSubList(); if (pOL!=nullptr && !pSource->Is3DObj()) { // get first non-group object from group SdrObjListIter aIter(*pOL,SdrIterMode::DeepNoGroups); pSource=aIter.Next(); } } if(pSource && pDest) { SfxItemSet aSet(mpModel->GetItemPool(), svl::Items{}); aSet.Put(pSource->GetMergedItemSet()); pDest->ClearMergedItem(); pDest->SetMergedItemSet(aSet); pDest->NbcSetLayer(pSource->GetLayer()); pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), true); } } bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj) { // new condition IsLine() to be able to combine simple Lines bool bIsLine(false); const SdrPathObj* pPath = dynamic_cast< const SdrPathObj*>( pObj ); if(pPath) { bIsLine = pPath->IsLine(); } SdrObjTransformInfoRec aInfo; pObj->TakeObjInfo(aInfo); return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine); } bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj) { SdrObjList* pOL = pObj->GetSubList(); if(pOL && !pObj->Is3DObj()) { SdrObjListIter aIter(*pOL, SdrIterMode::DeepNoGroups); while(aIter.IsMore()) { SdrObject* pObj1 = aIter.Next(); // all members of a group have to be convertible if(!ImpCanConvertForCombine1(pObj1)) { return false; } } } else { if(!ImpCanConvertForCombine1(pObj)) { return false; } } return true; } basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj) { basegfx::B2DPolyPolygon aRetval; const SdrPathObj* pPath = dynamic_cast( pObj ); if(pPath && !pObj->GetOutlinerParaObject()) { aRetval = pPath->GetPathPoly(); } else { SdrObject* pConvObj = pObj->ConvertToPolyObj(true/*bCombine*/, false); if(pConvObj) { SdrObjList* pOL = pConvObj->GetSubList(); if(pOL) { SdrObjListIter aIter(*pOL, SdrIterMode::DeepNoGroups); while(aIter.IsMore()) { SdrObject* pObj1 = aIter.Next(); pPath = dynamic_cast( pObj1 ); if(pPath) { aRetval.append(pPath->GetPathPoly()); } } } else { pPath = dynamic_cast( pConvObj ); if(pPath) { aRetval = pPath->GetPathPoly(); } } SdrObject::Free( pConvObj ); } } return aRetval; } basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj) { SdrObjList* pOL = pObj->GetSubList(); if(pOL && !pObj->Is3DObj()) { basegfx::B2DPolyPolygon aRetval; SdrObjListIter aIter(*pOL, SdrIterMode::DeepNoGroups); while(aIter.IsMore()) { SdrObject* pObj1 = aIter.Next(); aRetval.append(ImpGetPolyPolygon1(pObj1)); } return aRetval; } else { return ImpGetPolyPolygon1(pObj); } } basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) { const sal_uInt32 nPolyCount(rPolyPolygon.count()); if(0 == nPolyCount) { return basegfx::B2DPolygon(); } else if(1 == nPolyCount) { return rPolyPolygon.getB2DPolygon(0); } else { basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0)); for(sal_uInt32 a(1); a < nPolyCount; a++) { basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a)); if(aRetval.count()) { if(aCandidate.count()) { const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0)); const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1)); const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0)); const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1)); const double fRACA(basegfx::B2DVector(aCA - aRA).getLength()); const double fRACB(basegfx::B2DVector(aCB - aRA).getLength()); const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength()); const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength()); const double fSmallestRA(fRACA < fRACB ? fRACA : fRACB); const double fSmallestRB(fRBCA < fRBCB ? fRBCA : fRBCB); if(fSmallestRA < fSmallestRB) { // flip result aRetval.flip(); } const double fSmallestCA(fRACA < fRBCA ? fRACA : fRBCA); const double fSmallestCB(fRACB < fRBCB ? fRACB : fRBCB); if(fSmallestCB < fSmallestCA) { // flip candidate aCandidate.flip(); } // append candidate to retval aRetval.append(aCandidate); } } else { aRetval = aCandidate; } } return aRetval; } } // for distribution dialog function struct ImpDistributeEntry { SdrObject* mpObj; sal_Int32 mnPos; sal_Int32 mnLength; }; typedef vector< ImpDistributeEntry*> ImpDistributeEntryList; void SdrEditView::DistributeMarkedObjects() { const size_t nMark(GetMarkedObjectCount()); if(nMark > 2) { SfxItemSet aNewAttr(mpModel->GetItemPool()); SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); if(pFact) { ScopedVclPtr pDlg(pFact->CreateSvxDistributeDialog(aNewAttr)); DBG_ASSERT(pDlg, "Dialog creation failed!"); sal_uInt16 nResult = pDlg->Execute(); if(nResult == RET_OK) { SvxDistributeHorizontal eHor = pDlg->GetDistributeHor(); SvxDistributeVertical eVer = pDlg->GetDistributeVer(); ImpDistributeEntryList aEntryList; ImpDistributeEntryList::iterator itEntryList; sal_uInt32 nFullLength; const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(); if(eHor != SvxDistributeHorizontal::NONE) { // build sorted entry list nFullLength = 0; for( size_t a = 0; a < nMark; ++a ) { SdrMark* pMark = GetSdrMarkByIndex(a); ImpDistributeEntry* pNew = new ImpDistributeEntry; pNew->mpObj = pMark->GetMarkedSdrObj(); switch(eHor) { case SvxDistributeHorizontal::Left: { pNew->mnPos = pNew->mpObj->GetSnapRect().Left(); break; } case SvxDistributeHorizontal::Center: { pNew->mnPos = (pNew->mpObj->GetSnapRect().Right() + pNew->mpObj->GetSnapRect().Left()) / 2; break; } case SvxDistributeHorizontal::Distance: { pNew->mnLength = pNew->mpObj->GetSnapRect().GetWidth() + 1; nFullLength += pNew->mnLength; pNew->mnPos = (pNew->mpObj->GetSnapRect().Right() + pNew->mpObj->GetSnapRect().Left()) / 2; break; } case SvxDistributeHorizontal::Right: { pNew->mnPos = pNew->mpObj->GetSnapRect().Right(); break; } default: break; } for ( itEntryList = aEntryList.begin(); itEntryList < aEntryList.end() && (*itEntryList)->mnPos < pNew->mnPos; ++itEntryList ) {}; if ( itEntryList < aEntryList.end() ) aEntryList.insert( itEntryList, pNew ); else aEntryList.push_back( pNew ); } if(eHor == SvxDistributeHorizontal::Distance) { // calculate room in-between sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1; double fStepWidth = ((double)nWidth - (double)nFullLength) / (double)(aEntryList.size() - 1); double fStepStart = (double)aEntryList[ 0 ]->mnPos; fStepStart += fStepWidth + (double)((aEntryList[ 0 ]->mnLength + aEntryList[ 1 ]->mnLength) / 2); // move entries 1..n-1 for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i ) { ImpDistributeEntry* pCurr = aEntryList[ i ]; ImpDistributeEntry* pNext = aEntryList[ i + 1]; sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos; if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj)); pCurr->mpObj->Move(Size(nDelta, 0)); fStepStart += fStepWidth + (double)((pCurr->mnLength + pNext->mnLength) / 2); } } else { // calculate distances sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ]->mnPos - aEntryList[ 0 ]->mnPos; double fStepWidth = (double)nWidth / (double)(aEntryList.size() - 1); double fStepStart = (double)aEntryList[ 0 ]->mnPos; fStepStart += fStepWidth; // move entries 1..n-1 for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i ) { ImpDistributeEntry* pCurr = aEntryList[ i ]; sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos; if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj)); pCurr->mpObj->Move(Size(nDelta, 0)); fStepStart += fStepWidth; } } // clear list for (ImpDistributeEntry* p : aEntryList) delete p; aEntryList.clear(); } if(eVer != SvxDistributeVertical::NONE) { // build sorted entry list nFullLength = 0; for( size_t a = 0; a < nMark; ++a ) { SdrMark* pMark = GetSdrMarkByIndex(a); ImpDistributeEntry* pNew = new ImpDistributeEntry; pNew->mpObj = pMark->GetMarkedSdrObj(); switch(eVer) { case SvxDistributeVertical::Top: { pNew->mnPos = pNew->mpObj->GetSnapRect().Top(); break; } case SvxDistributeVertical::Center: { pNew->mnPos = (pNew->mpObj->GetSnapRect().Bottom() + pNew->mpObj->GetSnapRect().Top()) / 2; break; } case SvxDistributeVertical::Distance: { pNew->mnLength = pNew->mpObj->GetSnapRect().GetHeight() + 1; nFullLength += pNew->mnLength; pNew->mnPos = (pNew->mpObj->GetSnapRect().Bottom() + pNew->mpObj->GetSnapRect().Top()) / 2; break; } case SvxDistributeVertical::Bottom: { pNew->mnPos = pNew->mpObj->GetSnapRect().Bottom(); break; } default: break; } for ( itEntryList = aEntryList.begin(); itEntryList < aEntryList.end() && (*itEntryList)->mnPos < pNew->mnPos; ++itEntryList ) {}; if ( itEntryList < aEntryList.end() ) aEntryList.insert( itEntryList, pNew ); else aEntryList.push_back( pNew ); } if(eVer == SvxDistributeVertical::Distance) { // calculate room in-between sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1; double fStepWidth = ((double)nHeight - (double)nFullLength) / (double)(aEntryList.size() - 1); double fStepStart = (double)aEntryList[ 0 ]->mnPos; fStepStart += fStepWidth + (double)((aEntryList[ 0 ]->mnLength + aEntryList[ 1 ]->mnLength) / 2); // move entries 1..n-1 for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i) { ImpDistributeEntry* pCurr = aEntryList[ i ]; ImpDistributeEntry* pNext = aEntryList[ i + 1 ]; sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos; if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj)); pCurr->mpObj->Move(Size(0, nDelta)); fStepStart += fStepWidth + (double)((pCurr->mnLength + pNext->mnLength) / 2); } } else { // calculate distances sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ]->mnPos - aEntryList[ 0 ]->mnPos; double fStepWidth = (double)nHeight / (double)(aEntryList.size() - 1); double fStepStart = (double)aEntryList[ 0 ]->mnPos; fStepStart += fStepWidth; // move entries 1..n-1 for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i) { ImpDistributeEntry* pCurr = aEntryList[ i ]; sal_Int32 nDelta = (sal_Int32)(fStepStart + 0.5) - pCurr->mnPos; if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pCurr->mpObj)); pCurr->mpObj->Move(Size(0, nDelta)); fStepStart += fStepWidth; } } // clear list for (ImpDistributeEntry* p : aEntryList) delete p; aEntryList.clear(); } // UNDO-Comment and end of UNDO mpModel->SetUndoComment(ImpGetResStr(STR_DistributeMarkedObjects)); if( bUndo ) EndUndo(); } } } } void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode) { // #i73441# check content if(!AreObjectsMarked()) return; SdrMarkList aRemove; SortMarkedObjects(); const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo(); size_t nInsPos = SAL_MAX_SIZE; const SdrObject* pAttrObj = nullptr; basegfx::B2DPolyPolygon aMergePolyPolygonA; basegfx::B2DPolyPolygon aMergePolyPolygonB; SdrObjList* pInsOL = nullptr; SdrPageView* pInsPV = nullptr; bool bFirstObjectComplete(false); // make sure selected objects are contour objects // since now basegfx::utils::adaptiveSubdivide() is used, it is no longer // necessary to use ConvertMarkedToPolyObj which will subdivide curves using the old // mechanisms. In a next step the polygon clipper will even be able to clip curves... // ConvertMarkedToPolyObj(true); ConvertMarkedToPathObj(true); OSL_ENSURE(AreObjectsMarked(), "no more objects selected after preparations (!)"); for(size_t a=0; aGetMarkedSdrObj(); if(ImpCanConvertForCombine(pObj)) { if(!pAttrObj) pAttrObj = pObj; nInsPos = pObj->GetOrdNum() + 1; pInsPV = pM->GetPageView(); pInsOL = pObj->GetObjList(); // #i76891# use single iteration from SJ here which works on SdrObjects and takes // groups into account by itself SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups); while(aIter.IsMore()) { SdrObject* pCandidate = aIter.Next(); SdrPathObj* pPathObj = dynamic_cast( pCandidate ); if(pPathObj) { basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly()); // #i76891# unfortunately ConvertMarkedToPathObj has converted all // involved polygon data to curve segments, even if not necessary. // It is better to try to reduce to more simple polygons. aTmpPoly = basegfx::utils::simplifyCurveSegments(aTmpPoly); // for each part polygon as preparation, remove self-intersections // correct orientations and get rid of possible neutral polygons. aTmpPoly = basegfx::utils::prepareForPolygonOperation(aTmpPoly); if(!bFirstObjectComplete) { // #i111987# Also need to collect ORed source shape when more than // a single polygon is involved if(aMergePolyPolygonA.count()) { aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly); } else { aMergePolyPolygonA = aTmpPoly; } } else { if(aMergePolyPolygonB.count()) { // to topologically correctly collect the 2nd polygon // group it is necessary to OR the parts (each is seen as // XOR-FillRule polygon and they are drawn over each-other) aMergePolyPolygonB = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly); } else { aMergePolyPolygonB = aTmpPoly; } } } } // was there something added to the first polygon? if(!bFirstObjectComplete && aMergePolyPolygonA.count()) { bFirstObjectComplete = true; } // move object to temporary delete list aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView())); } } switch(eMode) { case SdrMergeMode::Merge: { // merge all contained parts (OR) static bool bTestXOR(false); if(bTestXOR) { aMergePolyPolygonA = basegfx::utils::solvePolygonOperationXor(aMergePolyPolygonA, aMergePolyPolygonB); } else { aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB); } break; } case SdrMergeMode::Subtract: { // Subtract B from A aMergePolyPolygonA = basegfx::utils::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB); break; } case SdrMergeMode::Intersect: { // AND B and A aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB); break; } } // #i73441# check insert list before taking actions if(pInsOL) { SdrPathObj* pPath = new SdrPathObj(OBJ_PATHFILL, aMergePolyPolygonA); ImpCopyAttributes(pAttrObj, pPath); pInsOL->InsertObject(pPath, nInsPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath)); // #i124760# To have a correct selection with only the new object it is necessary to // unmark all objects first. If not doing so, there may remain invalid pointers to objects //TTTT:Not needed for aw080 (!) UnmarkAllObj(pInsPV); MarkObj(pPath, pInsPV, false, true); } aRemove.ForceSort(); switch(eMode) { case SdrMergeMode::Merge: { SetUndoComment( ImpGetResStr(STR_EditMergeMergePoly), aRemove.GetMarkDescription()); break; } case SdrMergeMode::Subtract: { SetUndoComment( ImpGetResStr(STR_EditMergeSubstractPoly), aRemove.GetMarkDescription()); break; } case SdrMergeMode::Intersect: { SetUndoComment( ImpGetResStr(STR_EditMergeIntersectPoly), aRemove.GetMarkDescription()); break; } } DeleteMarkedList(aRemove); if( bUndo ) EndUndo(); } void SdrEditView::EqualizeMarkedObjects(bool bWidth) { const SdrMarkList& rMarkList = GetMarkedObjectList(); size_t nMarked = rMarkList.GetMarkCount(); if (nMarked < 2) return; size_t nLastSelected = 0; sal_Int64 nLastSelectedTime = rMarkList.GetMark(0)->getTimeStamp(); for (size_t a = 1; a < nMarked; ++a) { sal_Int64 nCandidateTime = rMarkList.GetMark(a)->getTimeStamp(); if (nCandidateTime > nLastSelectedTime) { nLastSelectedTime = nCandidateTime; nLastSelected = a; } } SdrObject* pLastSelectedObj = rMarkList.GetMark(nLastSelected)->GetMarkedSdrObj(); Size aLastRectSize(pLastSelectedObj->GetLogicRect().GetSize()); const bool bUndo = IsUndoEnabled(); if (bUndo) BegUndo(); for (size_t a = 0; a < nMarked; ++a) { if (a == nLastSelected) continue; SdrMark* pM = rMarkList.GetMark(a); SdrObject* pObj = pM->GetMarkedSdrObj(); tools::Rectangle aLogicRect(pObj->GetLogicRect()); Size aLogicRectSize(aLogicRect.GetSize()); if (bWidth) aLogicRectSize.Width() = aLastRectSize.Width(); else aLogicRectSize.Height() = aLastRectSize.Height(); aLogicRect.SetSize(aLogicRectSize); if (bUndo) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj)); pObj->SetLogicRect(aLogicRect); } SetUndoComment( ImpGetResStr(bWidth ? STR_EqualizeWidthMarkedObjects : STR_EqualizeHeightMarkedObjects), rMarkList.GetMarkDescription()); if (bUndo) EndUndo(); } void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly) { // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would // create a 2nd Undo-action and Undo-Comment. bool bUndo = IsUndoEnabled(); // Undo-String will be set later if( bUndo ) BegUndo("", "", bNoPolyPoly ? SdrRepeatFunc::CombineOnePoly : SdrRepeatFunc::CombinePolyPoly); // #105899# First, guarantee that all objects are converted to polyobjects, // especially for SdrGrafObj with bitmap filling this is necessary to not // lose the bitmap filling. // #i12392# // ConvertMarkedToPolyObj was too strong here, it will lose quality and // information when curve objects are combined. This can be replaced by // using ConvertMarkedToPathObj without changing the previous fix. // #i21250# // Instead of simply passing true as LineToArea, use bNoPolyPoly as info // if this command is a 'Combine' or a 'Connect' command. On Connect it's true. // To not concert line segments with a set line width to polygons in that case, // use this info. Do not convert LineToArea on Connect commands. // ConvertMarkedToPathObj(!bNoPolyPoly); // This is used for Combine and Connect. In no case it is necessary to force // the content to curve, but it is also not good to force to polygons. Thus, // curve is the less information losing one. Remember: This place is not // used for merge. // LineToArea is never necessary, both commands are able to take over the // set line style and to display it correctly. Thus, i will use a // ConvertMarkedToPathObj with a false in any case. Only drawback is that // simple polygons will be changed to curves, but with no information loss. ConvertMarkedToPathObj(false /* bLineToArea */); // continue as before basegfx::B2DPolyPolygon aPolyPolygon; SdrObjList* pAktOL = nullptr; SdrMarkList aRemoveMerker; SortMarkedObjects(); size_t nInsPos = SAL_MAX_SIZE; SdrObjList* pInsOL = nullptr; SdrPageView* pInsPV = nullptr; const SdrObject* pAttrObj = nullptr; for(size_t a = GetMarkedObjectCount(); a; ) { --a; SdrMark* pM = GetSdrMarkByIndex(a); SdrObject* pObj = pM->GetMarkedSdrObj(); SdrObjList* pThisOL = pObj->GetObjList(); if(pAktOL != pThisOL) { pAktOL = pThisOL; } if(ImpCanConvertForCombine(pObj)) { // remember objects to be able to copy attributes pAttrObj = pObj; // unfortunately ConvertMarkedToPathObj has converted all // involved polygon data to curve segments, even if not necessary. // It is better to try to reduce to more simple polygons. basegfx::B2DPolyPolygon aTmpPoly(basegfx::utils::simplifyCurveSegments(ImpGetPolyPolygon(pObj))); aPolyPolygon.insert(0L, aTmpPoly); if(!pInsOL) { nInsPos = pObj->GetOrdNum() + 1; pInsPV = pM->GetPageView(); pInsOL = pObj->GetObjList(); } aRemoveMerker.InsertEntry(SdrMark(pObj, pM->GetPageView())); } } if(bNoPolyPoly) { basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon)); aPolyPolygon.clear(); aPolyPolygon.append(aCombinedPolygon); } const sal_uInt32 nPolyCount(aPolyPolygon.count()); if (nPolyCount && pAttrObj) { SdrObjKind eKind = OBJ_PATHFILL; if(nPolyCount > 1) { aPolyPolygon.setClosed(true); } else { // check for Polyline const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0)); const sal_uInt32 nPointCount(aPolygon.count()); if(nPointCount <= 2) { eKind = OBJ_PATHLINE; } else { if(!aPolygon.isClosed()) { const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0)); const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1)); const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength()); const double fJoinTolerance(10.0); if(fDistance < fJoinTolerance) { aPolyPolygon.setClosed(true); } else { eKind = OBJ_PATHLINE; } } } } SdrPathObj* pPath = new SdrPathObj(eKind,aPolyPolygon); // attributes of the lowest object ImpCopyAttributes(pAttrObj, pPath); // If LineStyle of pAttrObj is drawing::LineStyle_NONE force to drawing::LineStyle_SOLID to make visible. const drawing::LineStyle eLineStyle = pAttrObj->GetMergedItem(XATTR_LINESTYLE).GetValue(); const drawing::FillStyle eFillStyle = pAttrObj->GetMergedItem(XATTR_FILLSTYLE).GetValue(); // Take fill style/closed state of pAttrObj in account when deciding to change the line style bool bIsClosedPathObj(dynamic_cast( pAttrObj) != nullptr && static_cast(pAttrObj)->IsClosed()); if(drawing::LineStyle_NONE == eLineStyle && (drawing::FillStyle_NONE == eFillStyle || !bIsClosedPathObj)) { pPath->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID)); } pInsOL->InsertObject(pPath,nInsPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath)); // Here was a severe error: Without UnmarkAllObj, the new object was marked // additionally to the two ones which are deleted below. As long as those are // in the UNDO there is no problem, but as soon as they get deleted, the // MarkList will contain deleted objects -> GPF. UnmarkAllObj(pInsPV); MarkObj(pPath, pInsPV, false, true); } // build an UndoComment from the objects actually used aRemoveMerker.ForceSort(); // important for remove (see below) if( bUndo ) SetUndoComment(ImpGetResStr(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveMerker.GetMarkDescription()); // remove objects actually used from the list DeleteMarkedList(aRemoveMerker); if( bUndo ) EndUndo(); } // Dismantle bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, bool bMakeLines) { bool bCan(false); const sal_uInt32 nPolygonCount(rPpolyPolygon.count()); if(nPolygonCount >= 2) { // #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon bCan = true; } else if(bMakeLines && 1 == nPolygonCount) { // #i69172# ..or with at least 2 edges (curves or lines) const basegfx::B2DPolygon aPolygon(rPpolyPolygon.getB2DPolygon(0)); const sal_uInt32 nPointCount(aPolygon.count()); if(nPointCount > 2) { bCan = true; } } return bCan; } bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, bool bMakeLines) { bool bOtherObjs(false); // true=objects other than PathObj's existent bool bMin1PolyPoly(false); // true=at least 1 tools::PolyPolygon with more than one Polygon existent SdrObjList* pOL = pObj->GetSubList(); if(pOL) { // group object -- check all members if they're PathObjs SdrObjListIter aIter(*pOL, SdrIterMode::DeepNoGroups); while(aIter.IsMore() && !bOtherObjs) { const SdrObject* pObj1 = aIter.Next(); const SdrPathObj* pPath = dynamic_cast( pObj1 ); if(pPath) { if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines)) { bMin1PolyPoly = true; } SdrObjTransformInfoRec aInfo; pObj1->TakeObjInfo(aInfo); if(!aInfo.bCanConvToPath) { // happens e. g. in the case of FontWork bOtherObjs = true; } } else { bOtherObjs = true; } } } else { const SdrPathObj* pPath = dynamic_cast(pObj); const SdrObjCustomShape* pCustomShape = dynamic_cast(pObj); // #i37011# if(pPath) { if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines)) { bMin1PolyPoly = true; } SdrObjTransformInfoRec aInfo; pObj->TakeObjInfo(aInfo); // new condition IsLine() to be able to break simple Lines if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine()) { // happens e. g. in the case of FontWork bOtherObjs = true; } } else if(pCustomShape) { if(bMakeLines) { // allow break command bMin1PolyPoly = true; } } else { bOtherObjs = true; } } return bMin1PolyPoly && !bOtherObjs; } void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, size_t& rPos, SdrPageView* pPV, bool bMakeLines) { const SdrPathObj* pSrcPath = dynamic_cast( pObj ); const SdrObjCustomShape* pCustomShape = dynamic_cast( pObj ); const bool bUndo = IsUndoEnabled(); if(pSrcPath) { // #i74631# redesigned due to XpolyPolygon removal and explicit constructors SdrObject* pLast = nullptr; // to be able to apply OutlinerParaObject const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly()); const sal_uInt32 nPolyCount(rPolyPolygon.count()); for(sal_uInt32 a(0); a < nPolyCount; a++) { const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a)); const sal_uInt32 nPointCount(rCandidate.count()); if(!bMakeLines || nPointCount < 2) { SdrPathObj* pPath = new SdrPathObj((SdrObjKind)pSrcPath->GetObjIdentifier(), basegfx::B2DPolyPolygon(rCandidate)); ImpCopyAttributes(pSrcPath, pPath); pLast = pPath; rOL.InsertObject(pPath, rPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true)); MarkObj(pPath, pPV, false, true); rPos++; } else { const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); for(sal_uInt32 b(0); b < nLoopCount; b++) { SdrObjKind eKind(OBJ_PLIN); basegfx::B2DPolygon aNewPolygon; const sal_uInt32 nNextIndex((b + 1) % nPointCount); aNewPolygon.append(rCandidate.getB2DPoint(b)); if(rCandidate.areControlPointsUsed()) { aNewPolygon.appendBezierSegment( rCandidate.getNextControlPoint(b), rCandidate.getPrevControlPoint(nNextIndex), rCandidate.getB2DPoint(nNextIndex)); eKind = OBJ_PATHLINE; } else { aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex)); } SdrPathObj* pPath = new SdrPathObj(eKind, basegfx::B2DPolyPolygon(aNewPolygon)); ImpCopyAttributes(pSrcPath, pPath); pLast = pPath; rOL.InsertObject(pPath, rPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true)); MarkObj(pPath, pPV, false, true); rPos++; } } } if(pLast && pSrcPath->GetOutlinerParaObject()) { pLast->SetOutlinerParaObject(new OutlinerParaObject(*pSrcPath->GetOutlinerParaObject())); } } else if(pCustomShape) { if(bMakeLines) { // break up custom shape const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape(); if(pReplacement) { SdrObject* pCandidate = pReplacement->Clone(); DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)"); pCandidate->SetModel(pCustomShape->GetModel()); if(pCustomShape->GetMergedItem(SDRATTR_SHADOW).GetValue()) { if(dynamic_cast( pReplacement) != nullptr) { pCandidate->SetMergedItem(makeSdrShadowItem(true)); } } rOL.InsertObject(pCandidate, rPos); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true)); MarkObj(pCandidate, pPV, false, true); if(pCustomShape->HasText() && !pCustomShape->IsTextPath()) { // #i37011# also create a text object and add at rPos + 1 SdrObject* pTextObj = SdrObjFactory::MakeNewObject( pCustomShape->GetObjInventor(), OBJ_TEXT, nullptr, pCustomShape->GetModel()); // Copy text content OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject(); if(pParaObj) { pTextObj->NbcSetOutlinerParaObject(new OutlinerParaObject(*pParaObj)); } // copy all attributes SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet()); // clear fill and line style aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); // get the text bounds and set at text object tools::Rectangle aTextBounds = pCustomShape->GetSnapRect(); if(pCustomShape->GetTextBounds(aTextBounds)) { pTextObj->SetSnapRect(aTextBounds); } // if rotated, copy GeoStat, too. const GeoStat& rSourceGeo = pCustomShape->GetGeoStat(); if(rSourceGeo.nRotationAngle) { pTextObj->NbcRotate( pCustomShape->GetSnapRect().Center(), rSourceGeo.nRotationAngle, rSourceGeo.nSin, rSourceGeo.nCos); } // set modified ItemSet at text object pTextObj->SetMergedItemSet(aTargetItemSet); // insert object rOL.InsertObject(pTextObj, rPos + 1); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true)); MarkObj(pTextObj, pPV, false, true); } } } } } void SdrEditView::DismantleMarkedObjects(bool bMakeLines) { // temporary MarkList SdrMarkList aRemoveMerker; SortMarkedObjects(); const bool bUndo = IsUndoEnabled(); if( bUndo ) { // comment is constructed later BegUndo("", "", bMakeLines ? SdrRepeatFunc::DismantleLines : SdrRepeatFunc::DismantlePolys); } SdrObjList* pOL0=nullptr; for (size_t nm=GetMarkedObjectCount(); nm>0;) { --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pObj=pM->GetMarkedSdrObj(); SdrPageView* pPV=pM->GetPageView(); SdrObjList* pOL=pObj->GetObjList(); if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // make sure OrdNums are correct! if (ImpCanDismantle(pObj,bMakeLines)) { aRemoveMerker.InsertEntry(SdrMark(pObj,pM->GetPageView())); const size_t nPos0=pObj->GetOrdNumDirect(); size_t nPos=nPos0+1; SdrObjList* pSubList=pObj->GetSubList(); if (pSubList!=nullptr && !pObj->Is3DObj()) { SdrObjListIter aIter(*pSubList,SdrIterMode::DeepNoGroups); while (aIter.IsMore()) { const SdrObject* pObj1=aIter.Next(); ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines); } } else { ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines); } if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,true)); pOL->RemoveObject(nPos0); if( !bUndo ) SdrObject::Free(pObj); } } if( bUndo ) { // construct UndoComment from objects actually used SetUndoComment(ImpGetResStr(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveMerker.GetMarkDescription()); // remove objects actually used from the list EndUndo(); } } // Group void SdrEditView::GroupMarked() { if (AreObjectsMarked()) { SortMarkedObjects(); const bool bUndo = IsUndoEnabled(); if( bUndo ) { BegUndo(ImpGetResStr(STR_EditGroup),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Group); for(size_t nm = GetMarkedObjectCount(); nm>0; ) { // add UndoActions for all affected objects --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pObj = pM->GetMarkedSdrObj(); std::vector< SdrUndoAction* > vConnectorUndoActions( CreateConnectorUndo( *pObj ) ); AddUndoActions( vConnectorUndoActions ); AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject( *pObj )); } } SdrMarkList aNewMark; SdrPageView* pPV = GetSdrPageView(); if(pPV) { SdrObjList* pAktLst=pPV->GetObjList(); SdrObjList* pSrcLst=pAktLst; SdrObjList* pSrcLst0=pSrcLst; // make sure OrdNums are correct if (pSrcLst->IsObjOrdNumsDirty()) pSrcLst->RecalcObjOrdNums(); SdrObject* pGrp=nullptr; SdrObjList* pDstLst=nullptr; // if all selected objects come from foreign object lists. // the group object is the last one in the list. size_t nInsPos=pSrcLst->GetObjCount(); bool bNeedInsPos=true; for (size_t nm=GetMarkedObjectCount(); nm>0;) { --nm; SdrMark* pM=GetSdrMarkByIndex(nm); if (pM->GetPageView()==pPV) { if (pGrp==nullptr) { if (pGrp==nullptr) pGrp=new SdrObjGroup; pDstLst=pGrp->GetSubList(); DBG_ASSERT(pDstLst!=nullptr,"Alleged group object doesn't return object list."); } SdrObject* pObj=pM->GetMarkedSdrObj(); pSrcLst=pObj->GetObjList(); if (pSrcLst!=pSrcLst0) { if (pSrcLst->IsObjOrdNumsDirty()) pSrcLst->RecalcObjOrdNums(); } bool bForeignList=pSrcLst!=pAktLst; if (!bForeignList && bNeedInsPos) { nInsPos=pObj->GetOrdNum(); // this way, all ObjOrdNum of the page are set nInsPos++; bNeedInsPos=false; } pSrcLst->RemoveObject(pObj->GetOrdNumDirect()); if (!bForeignList) nInsPos--; // correct InsertPos pDstLst->InsertObject(pObj,0); GetMarkedObjectListWriteAccess().DeleteMark(nm); pSrcLst0=pSrcLst; } } if (pGrp!=nullptr) { aNewMark.InsertEntry(SdrMark(pGrp,pPV)); const size_t nCount=pDstLst->GetObjCount(); pAktLst->InsertObject(pGrp,nInsPos); if( bUndo ) { AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // no recalculation! for (size_t no=0; noGetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no))); } } } } GetMarkedObjectListWriteAccess().Merge(aNewMark); MarkListHasChanged(); if( bUndo ) EndUndo(); } } // Ungroup void SdrEditView::UnGroupMarked() { SdrMarkList aNewMark; const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo("", "", SdrRepeatFunc::Ungroup); size_t nCount=0; OUString aName1; OUString aName; bool bNameOk=false; for (size_t nm=GetMarkedObjectCount(); nm>0;) { --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pGrp=pM->GetMarkedSdrObj(); SdrObjList* pSrcLst=pGrp->GetSubList(); if (pSrcLst!=nullptr) { nCount++; if (nCount==1) { aName = pGrp->TakeObjNameSingul(); // retrieve name of group aName1 = pGrp->TakeObjNamePlural(); // retrieve name of group bNameOk=true; } else { if (nCount==2) aName=aName1; // set plural name if (bNameOk) { OUString aStr(pGrp->TakeObjNamePlural()); // retrieve name of group if (aStr != aName) bNameOk = false; } } size_t nDstCnt=pGrp->GetOrdNum(); SdrObjList* pDstLst=pM->GetPageView()->GetObjList(); // FIRST move contained objects to parent of group, so that // the contained objects are NOT migrated to the UNDO-ItemPool // when AddUndo(new SdrUndoDelObj(*pGrp)) is called. const size_t nObjCount=pSrcLst->GetObjCount(); if( bUndo ) { for (size_t no=nObjCount; no>0;) { no--; SdrObject* pObj=pSrcLst->GetObj(no); AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject(*pObj)); } } for (size_t no=0; noRemoveObject(0); pDstLst->InsertObject(pObj,nDstCnt); if( bUndo ) AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true)); nDstCnt++; // No SortCheck when inserting into MarkList, because that would // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum(): aNewMark.InsertEntry(SdrMark(pObj,pM->GetPageView()),false); } if( bUndo ) { // Now it is safe to add the delete-UNDO which triggers the // MigrateItemPool now only for itself, not for the sub-objects. // nDstCnt is right, because previous inserts move group // object deeper and increase nDstCnt. AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp)); } pDstLst->RemoveObject(nDstCnt); if( !bUndo ) SdrObject::Free(pGrp); GetMarkedObjectListWriteAccess().DeleteMark(nm); } } if (nCount!=0) { if (!bNameOk) aName=ImpGetResStr(STR_ObjNamePluralGRUP); // Use the term "Group Objects," if different objects are grouped. SetUndoComment(ImpGetResStr(STR_EditUngroup),aName); } if( bUndo ) EndUndo(); if (nCount!=0) { GetMarkedObjectListWriteAccess().Merge(aNewMark,true); // Because of the sorting above, aNewMark is reversed MarkListHasChanged(); } } // ConvertToPoly SdrObject* SdrEditView::ImpConvertOneObj(SdrObject* pObj, bool bPath, bool bLineToArea) { SdrObject* pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea); if (pNewObj!=nullptr) { SdrObjList* pOL=pObj->GetObjList(); DBG_ASSERT(pOL!=nullptr,"ConvertTo: Object doesn't return object list"); if (pOL!=nullptr) { const bool bUndo = IsUndoEnabled(); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj)); pOL->ReplaceObject(pNewObj,pObj->GetOrdNum()); if( !bUndo ) SdrObject::Free(pObj); } } return pNewObj; } void SdrEditView::ImpConvertTo(bool bPath, bool bLineToArea) { if (AreObjectsMarked()) { bool bMrkChg = false; const size_t nMarkCount=GetMarkedObjectCount(); const char* pDscrID = nullptr; if(bLineToArea) { if(nMarkCount == 1) pDscrID = STR_EditConvToContour; else pDscrID = STR_EditConvToContours; BegUndo(ImpGetResStr(pDscrID), GetDescriptionOfMarkedObjects()); } else { if (bPath) { if (nMarkCount==1) pDscrID=STR_EditConvToCurve; else pDscrID=STR_EditConvToCurves; BegUndo(ImpGetResStr(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPath); } else { if (nMarkCount==1) pDscrID=STR_EditConvToPoly; else pDscrID=STR_EditConvToPolys; BegUndo(ImpGetResStr(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPoly); } } for (size_t nm=nMarkCount; nm>0;) { --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pObj=pM->GetMarkedSdrObj(); SdrPageView* pPV=pM->GetPageView(); if (pObj->IsGroupObject() && !pObj->Is3DObj()) { SdrObject* pGrp=pObj; SdrObjListIter aIter(*pGrp,SdrIterMode::DeepNoGroups); while (aIter.IsMore()) { pObj=aIter.Next(); ImpConvertOneObj(pObj,bPath,bLineToArea); } } else { SdrObject* pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea); if (pNewObj!=nullptr) { bMrkChg=true; GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj,pPV),nm); } } } EndUndo(); if (bMrkChg) AdjustMarkHdl(); if (bMrkChg) MarkListHasChanged(); } } void SdrEditView::ConvertMarkedToPathObj(bool bLineToArea) { ImpConvertTo(true, bLineToArea); } void SdrEditView::ConvertMarkedToPolyObj() { ImpConvertTo(false, false/*bLineToArea*/); } namespace { GDIMetaFile GetMetaFile(SdrGrafObj const * pGraf) { if (pGraf->HasGDIMetaFile()) return pGraf->GetTransformedGraphic(SdrGrafObjTransformsAttrs::COLOR|SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile(); assert(pGraf->isEmbeddedVectorGraphicData()); return pGraf->getMetafileFromEmbeddedVectorGraphicData(); } } // Metafile Import void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo) { const bool bUndo = IsUndoEnabled(); if( bUndo ) BegUndo("", "", SdrRepeatFunc::ImportMtf); SortMarkedObjects(); SdrMarkList aForTheDescription; SdrMarkList aNewMarked; const size_t nCount=GetMarkedObjectCount(); for (size_t nm=nCount; nm>0;) { // create Undo objects for all new objects // check for cancellation between the metafiles if( pProgrInfo != nullptr ) { pProgrInfo->SetNextObject(); if(!pProgrInfo->ReportActions(0)) break; } --nm; SdrMark* pM=GetSdrMarkByIndex(nm); SdrObject* pObj=pM->GetMarkedSdrObj(); SdrPageView* pPV=pM->GetPageView(); SdrObjList* pOL=pObj->GetObjList(); const size_t nInsPos=pObj->GetOrdNum()+1; SdrGrafObj* pGraf= dynamic_cast( pObj ); SdrOle2Obj* pOle2= dynamic_cast( pObj ); sal_uIntPtr nInsAnz=0; tools::Rectangle aLogicRect; if (pGraf && (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedVectorGraphicData())) { GDIMetaFile aMetaFile(GetMetaFile(pGraf)); if(aMetaFile.GetActionSize()) { aLogicRect = pGraf->GetLogicRect(); ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect); nInsAnz = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo); } } if ( pOle2!=nullptr && pOle2->GetGraphic() ) { aLogicRect = pOle2->GetLogicRect(); ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect); nInsAnz = aFilter.DoImport(pOle2->GetGraphic()->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo); } if (nInsAnz!=0) { // transformation GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat()); size_t nObj=nInsPos; if(aGeoStat.nShearAngle) { aGeoStat.RecalcTan(); } if(aGeoStat.nRotationAngle) { aGeoStat.RecalcSinCos(); } for (sal_uIntPtr i=0; iGetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj))); // update new MarkList SdrObject* pCandidate = pOL->GetObj(nObj); // apply original transformation if(aGeoStat.nShearAngle) { pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.nShearAngle, aGeoStat.nTan, false); } if(aGeoStat.nRotationAngle) { pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.nRotationAngle, aGeoStat.nSin, aGeoStat.nCos); } SdrMark aNewMark(pCandidate, pPV); aNewMarked.InsertEntry(aNewMark); nObj++; } aForTheDescription.InsertEntry(*pM); if( bUndo ) AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj)); // remove object from selection and delete GetMarkedObjectListWriteAccess().DeleteMark(TryToFindMarkedObject(pObj)); pOL->RemoveObject(nInsPos-1); if( !bUndo ) SdrObject::Free(pObj); } } if(aNewMarked.GetMarkCount()) { // create new selection for(size_t a = 0; a < aNewMarked.GetMarkCount(); ++a) { GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a)); } SortMarkedObjects(); } if( bUndo ) { SetUndoComment(ImpGetResStr(STR_EditImportMtf),aForTheDescription.GetMarkDescription()); EndUndo(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */