Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / svdraw / svdedtv2.cxx
blob11b4401349b8a70756890dbd2ee8125fba21b83a
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 <svx/svdedtv.hxx>
21 #include <svx/svdundo.hxx>
22 #include <svx/svdogrp.hxx>
23 #include <svx/svdoutl.hxx>
24 #include <svx/svdopath.hxx>
25 #include <svx/svdpage.hxx>
26 #include <svx/svdpagv.hxx>
27 #include <svx/svditer.hxx>
28 #include <svx/svdograf.hxx>
29 #include <svx/svdoole2.hxx>
30 #include <svx/dialmgr.hxx>
31 #include <svx/sdooitm.hxx>
32 #include <svx/sdshitm.hxx>
33 #include <svx/xfillit0.hxx>
34 #include <svx/xlineit0.hxx>
35 #include <svx/xtextit0.hxx>
36 #include "svdfmtf.hxx"
37 #include <svdpdf.hxx>
38 #include <svx/svdetc.hxx>
39 #include <editeng/outlobj.hxx>
40 #include <editeng/eeitem.hxx>
41 #include <basegfx/polygon/b2dpolypolygon.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <svx/svxdlg.hxx>
44 #include <svx/strings.hrc>
45 #include <svx/svdoashp.hxx>
46 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
47 #include <i18nutil/unicode.hxx>
48 #include <sal/log.hxx>
49 #include <tools/debug.hxx>
50 #include <memory>
51 #include <vector>
52 #include <vcl/graph.hxx>
53 #include <svx/svxids.hrc>
55 using namespace com::sun::star;
57 SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const
59 return nullptr;
62 SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const
64 return nullptr;
67 void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, size_t /*nOldPos*/, size_t /*nNewPos*/)
71 void SdrEditView::MovMarkedToTop()
73 const size_t nCount=GetMarkedObjectCount();
74 if (nCount==0)
75 return;
77 const bool bUndo = IsUndoEnabled();
79 if( bUndo )
80 BegUndo(SvxResId(STR_EditMovToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToTop);
82 SortMarkedObjects();
83 for (size_t nm=0; nm<nCount; ++nm)
84 { // All Ordnums have to be correct!
85 GetMarkedObjectByIndex(nm)->GetOrdNum();
87 bool bChg=false;
88 SdrObjList* pOL0=nullptr;
89 size_t nNewPos=0;
90 for (size_t nm=nCount; nm>0;)
92 --nm;
93 SdrMark* pM=GetSdrMarkByIndex(nm);
94 SdrObject* pObj=pM->GetMarkedSdrObj();
95 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
96 if (pOL!=pOL0)
98 nNewPos = pOL->GetObjCount()-1;
99 pOL0=pOL;
101 const size_t nNowPos = pObj->GetOrdNumDirect();
102 const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
103 size_t nCmpPos = nNowPos+1;
104 SdrObject* pMaxObj=GetMaxToTopObj(pObj);
105 if (pMaxObj!=nullptr)
107 size_t nMaxPos=pMaxObj->GetOrdNum();
108 if (nMaxPos!=0)
109 nMaxPos--;
110 if (nNewPos>nMaxPos)
111 nNewPos=nMaxPos; // neither go faster...
112 if (nNewPos<nNowPos)
113 nNewPos=nNowPos; // nor go in the other direction
115 bool bEnd=false;
116 while (nCmpPos<nNewPos && !bEnd)
118 SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
119 if (pCmpObj==nullptr)
121 OSL_FAIL("MovMarkedToTop(): Reference object not found.");
122 bEnd=true;
124 else if (pCmpObj==pMaxObj)
126 nNewPos=nCmpPos;
127 nNewPos--;
128 bEnd=true;
130 else if (rBR.IsOver(pCmpObj->GetCurrentBoundRect()))
132 nNewPos=nCmpPos;
133 bEnd=true;
135 else
137 nCmpPos++;
140 if (nNowPos!=nNewPos)
142 bChg=true;
143 pOL->SetObjectOrdNum(nNowPos,nNewPos);
144 if( bUndo )
145 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
146 ObjOrderChanged(pObj,nNowPos,nNewPos);
148 nNewPos--;
151 if( bUndo )
152 EndUndo();
154 if (bChg)
155 MarkListHasChanged();
158 void SdrEditView::MovMarkedToBtm()
160 const size_t nCount=GetMarkedObjectCount();
161 if (nCount==0)
162 return;
164 const bool bUndo = IsUndoEnabled();
166 if( bUndo )
167 BegUndo(SvxResId(STR_EditMovToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToBottom);
169 SortMarkedObjects();
170 for (size_t nm=0; nm<nCount; ++nm)
171 { // All Ordnums have to be correct!
172 GetMarkedObjectByIndex(nm)->GetOrdNum();
175 bool bChg=false;
176 SdrObjList* pOL0=nullptr;
177 size_t nNewPos=0;
178 for (size_t nm=0; nm<nCount; ++nm)
180 SdrMark* pM=GetSdrMarkByIndex(nm);
181 SdrObject* pObj=pM->GetMarkedSdrObj();
182 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
183 if (pOL!=pOL0)
185 nNewPos=0;
186 pOL0=pOL;
188 const size_t nNowPos = pObj->GetOrdNumDirect();
189 const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
190 size_t nCmpPos = nNowPos;
191 if (nCmpPos>0)
192 --nCmpPos;
193 SdrObject* pMaxObj=GetMaxToBtmObj(pObj);
194 if (pMaxObj!=nullptr)
196 const size_t nMinPos=pMaxObj->GetOrdNum()+1;
197 if (nNewPos<nMinPos)
198 nNewPos=nMinPos; // neither go faster...
199 if (nNewPos>nNowPos)
200 nNewPos=nNowPos; // nor go in the other direction
202 bool bEnd=false;
203 // nNewPos in this case is the "maximum" position
204 // the object may reach without going faster than the object before
205 // it (multiple selection).
206 while (nCmpPos>nNewPos && !bEnd)
208 SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
209 if (pCmpObj==nullptr)
211 OSL_FAIL("MovMarkedToBtm(): Reference object not found.");
212 bEnd=true;
214 else if (pCmpObj==pMaxObj)
216 nNewPos=nCmpPos;
217 nNewPos++;
218 bEnd=true;
220 else if (rBR.IsOver(pCmpObj->GetCurrentBoundRect()))
222 nNewPos=nCmpPos;
223 bEnd=true;
225 else
227 nCmpPos--;
230 if (nNowPos!=nNewPos)
232 bChg=true;
233 pOL->SetObjectOrdNum(nNowPos,nNewPos);
234 if( bUndo )
235 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
236 ObjOrderChanged(pObj,nNowPos,nNewPos);
238 nNewPos++;
241 if(bUndo)
242 EndUndo();
244 if(bChg)
245 MarkListHasChanged();
248 void SdrEditView::PutMarkedToTop()
250 PutMarkedInFrontOfObj(nullptr);
253 void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj)
255 const size_t nCount=GetMarkedObjectCount();
256 if (nCount==0)
257 return;
259 const bool bUndo = IsUndoEnabled();
260 if( bUndo )
261 BegUndo(SvxResId(STR_EditPutToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToTop);
263 SortMarkedObjects();
265 if (pRefObj!=nullptr)
267 // Make "in front of the object" work, even if the
268 // selected objects are already in front of the other object
269 const size_t nRefMark=TryToFindMarkedObject(pRefObj);
270 SdrMark aRefMark;
271 if (nRefMark!=SAL_MAX_SIZE)
273 aRefMark=*GetSdrMarkByIndex(nRefMark);
274 GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
276 PutMarkedToBtm();
277 if (nRefMark!=SAL_MAX_SIZE)
279 GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
280 SortMarkedObjects();
283 for (size_t nm=0; nm<nCount; ++nm)
284 { // All Ordnums have to be correct!
285 GetMarkedObjectByIndex(nm)->GetOrdNum();
287 bool bChg=false;
288 SdrObjList* pOL0=nullptr;
289 size_t nNewPos=0;
290 for (size_t nm=nCount; nm>0;)
292 --nm;
293 SdrMark* pM=GetSdrMarkByIndex(nm);
294 SdrObject* pObj=pM->GetMarkedSdrObj();
295 if (pObj!=pRefObj)
297 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
298 if (pOL!=pOL0)
300 nNewPos=pOL->GetObjCount()-1;
301 pOL0=pOL;
303 const size_t nNowPos=pObj->GetOrdNumDirect();
304 SdrObject* pMaxObj=GetMaxToTopObj(pObj);
305 if (pMaxObj!=nullptr)
307 size_t nMaxOrd=pMaxObj->GetOrdNum(); // sadly doesn't work any other way
308 if (nMaxOrd>0)
309 nMaxOrd--;
310 if (nNewPos>nMaxOrd)
311 nNewPos=nMaxOrd; // neither go faster...
312 if (nNewPos<nNowPos)
313 nNewPos=nNowPos; // nor go into the other direction
315 if (pRefObj!=nullptr)
317 if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject())
319 const size_t nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way
320 if (nNewPos>nMaxOrd)
321 nNewPos=nMaxOrd; // neither go faster...
322 if (nNewPos<nNowPos)
323 nNewPos=nNowPos; // nor go into the other direction
325 else
327 nNewPos=nNowPos; // different PageView, so don't change
330 if (nNowPos!=nNewPos)
332 bChg=true;
333 pOL->SetObjectOrdNum(nNowPos,nNewPos);
334 if( bUndo )
335 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
336 ObjOrderChanged(pObj,nNowPos,nNewPos);
338 nNewPos--;
339 } // if (pObj!=pRefObj)
340 } // for loop over all selected objects
342 if( bUndo )
343 EndUndo();
345 if(bChg)
346 MarkListHasChanged();
349 void SdrEditView::PutMarkedToBtm()
351 PutMarkedBehindObj(nullptr);
354 void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj)
356 const size_t nCount=GetMarkedObjectCount();
357 if (nCount==0)
358 return;
360 const bool bUndo = IsUndoEnabled();
362 if( bUndo )
363 BegUndo(SvxResId(STR_EditPutToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToBottom);
365 SortMarkedObjects();
366 if (pRefObj!=nullptr)
368 // Make "behind the object" work, even if the
369 // selected objects are already behind the other object
370 const size_t nRefMark=TryToFindMarkedObject(pRefObj);
371 SdrMark aRefMark;
372 if (nRefMark!=SAL_MAX_SIZE)
374 aRefMark=*GetSdrMarkByIndex(nRefMark);
375 GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
377 PutMarkedToTop();
378 if (nRefMark!=SAL_MAX_SIZE)
380 GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
381 SortMarkedObjects();
384 for (size_t nm=0; nm<nCount; ++nm) { // All Ordnums have to be correct!
385 GetMarkedObjectByIndex(nm)->GetOrdNum();
387 bool bChg=false;
388 SdrObjList* pOL0=nullptr;
389 size_t nNewPos=0;
390 for (size_t nm=0; nm<nCount; ++nm) {
391 SdrMark* pM=GetSdrMarkByIndex(nm);
392 SdrObject* pObj=pM->GetMarkedSdrObj();
393 if (pObj!=pRefObj) {
394 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
395 if (pOL!=pOL0) {
396 nNewPos=0;
397 pOL0=pOL;
399 const size_t nNowPos=pObj->GetOrdNumDirect();
400 SdrObject* pMinObj=GetMaxToBtmObj(pObj);
401 if (pMinObj!=nullptr) {
402 const size_t nMinOrd=pMinObj->GetOrdNum()+1; // sadly doesn't work any differently
403 if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
404 if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
406 if (pRefObj!=nullptr) {
407 if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject()) {
408 const size_t nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently
409 if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
410 if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
411 } else {
412 nNewPos=nNowPos; // different PageView, so don't change
415 if (nNowPos!=nNewPos) {
416 bChg=true;
417 pOL->SetObjectOrdNum(nNowPos,nNewPos);
418 if( bUndo )
419 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
420 ObjOrderChanged(pObj,nNowPos,nNewPos);
422 nNewPos++;
423 } // if (pObj!=pRefObj)
424 } // for loop over all selected objects
426 if(bUndo)
427 EndUndo();
429 if(bChg)
430 MarkListHasChanged();
434 void SdrEditView::ReverseOrderOfMarked()
436 SortMarkedObjects();
437 const size_t nMarkCount=GetMarkedObjectCount();
438 if (nMarkCount<=0)
439 return;
441 bool bChg=false;
443 bool bUndo = IsUndoEnabled();
444 if( bUndo )
445 BegUndo(SvxResId(STR_EditRevOrder),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ReverseOrder);
447 size_t a=0;
448 do {
449 // take into account selection across multiple PageViews
450 size_t b=a+1;
451 while (b<nMarkCount && GetSdrPageViewOfMarkedByIndex(b) == GetSdrPageViewOfMarkedByIndex(a)) ++b;
452 --b;
453 SdrObjList* pOL=GetSdrPageViewOfMarkedByIndex(a)->GetObjList();
454 size_t c=b;
455 if (a<c) { // make sure OrdNums aren't dirty
456 GetMarkedObjectByIndex(a)->GetOrdNum();
458 while (a<c) {
459 SdrObject* pObj1=GetMarkedObjectByIndex(a);
460 SdrObject* pObj2=GetMarkedObjectByIndex(c);
461 const size_t nOrd1=pObj1->GetOrdNumDirect();
462 const size_t nOrd2=pObj2->GetOrdNumDirect();
463 if( bUndo )
465 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2));
466 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1));
468 pOL->SetObjectOrdNum(nOrd1,nOrd2);
469 // Obj 2 has moved forward by one position, so now nOrd2-1
470 pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
471 // use Replace instead of SetOrdNum for performance reasons (recalculation of Ordnums)
472 ++a;
473 --c;
474 bChg=true;
476 a=b+1;
477 } while (a<nMarkCount);
479 if(bUndo)
480 EndUndo();
482 if(bChg)
483 MarkListHasChanged();
486 void SdrEditView::ImpCheckToTopBtmPossible()
488 const size_t nCount=GetMarkedObjectCount();
489 if (nCount==0)
490 return;
491 if (nCount==1)
492 { // special-casing for single selection
493 SdrObject* pObj=GetMarkedObjectByIndex(0);
494 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
495 SAL_WARN_IF(!pOL, "svx", "Object somehow has no ObjList");
496 size_t nMax = pOL ? pOL->GetObjCount() : 0;
497 size_t nMin = 0;
498 const size_t nObjNum=pObj->GetOrdNum();
499 SdrObject* pRestrict=GetMaxToTopObj(pObj);
500 if (pRestrict!=nullptr) {
501 const size_t nRestrict=pRestrict->GetOrdNum();
502 if (nRestrict<nMax) nMax=nRestrict;
504 pRestrict=GetMaxToBtmObj(pObj);
505 if (pRestrict!=nullptr) {
506 const size_t nRestrict=pRestrict->GetOrdNum();
507 if (nRestrict>nMin) nMin=nRestrict;
509 m_bToTopPossible=nObjNum<nMax-1;
510 m_bToBtmPossible=nObjNum>nMin;
511 } else { // multiple selection
512 SdrObjList* pOL0=nullptr;
513 size_t nPos0 = 0;
514 for (size_t nm = 0; !m_bToBtmPossible && nm<nCount; ++nm) { // check 'send to background'
515 SdrObject* pObj=GetMarkedObjectByIndex(nm);
516 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
517 if (pOL!=pOL0) {
518 nPos0 = 0;
519 pOL0=pOL;
521 const size_t nPos = pObj->GetOrdNum();
522 m_bToBtmPossible = nPos && (nPos-1 > nPos0);
523 nPos0 = nPos;
526 pOL0=nullptr;
527 nPos0 = SAL_MAX_SIZE;
528 for (size_t nm=nCount; !m_bToTopPossible && nm>0; ) { // check 'bring to front'
529 --nm;
530 SdrObject* pObj=GetMarkedObjectByIndex(nm);
531 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
532 if (pOL!=pOL0) {
533 nPos0=pOL->GetObjCount();
534 pOL0=pOL;
536 const size_t nPos = pObj->GetOrdNum();
537 m_bToTopPossible = nPos+1 < nPos0;
538 nPos0=nPos;
544 // Combine
547 void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
549 if (pSource!=nullptr) {
550 SdrObjList* pOL=pSource->GetSubList();
551 if (pOL!=nullptr && !pSource->Is3DObj()) { // get first non-group object from group
552 SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
553 pSource=aIter.Next();
557 if(!(pSource && pDest))
558 return;
560 SfxItemSet aSet(mpModel->GetItemPool(),
561 svl::Items<SDRATTR_START, SDRATTR_NOTPERSIST_FIRST-1,
562 SDRATTR_NOTPERSIST_LAST+1, SDRATTR_END,
563 EE_ITEMS_START, EE_ITEMS_END>{});
565 aSet.Put(pSource->GetMergedItemSet());
567 pDest->ClearMergedItem();
568 pDest->SetMergedItemSet(aSet);
570 pDest->NbcSetLayer(pSource->GetLayer());
571 pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), true);
574 bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj)
576 // new condition IsLine() to be able to combine simple Lines
577 bool bIsLine(false);
579 const SdrPathObj* pPath = dynamic_cast< const SdrPathObj*>( pObj );
581 if(pPath)
583 bIsLine = pPath->IsLine();
586 SdrObjTransformInfoRec aInfo;
587 pObj->TakeObjInfo(aInfo);
589 return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine);
592 bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj)
594 SdrObjList* pOL = pObj->GetSubList();
596 if(pOL && !pObj->Is3DObj())
598 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
600 while(aIter.IsMore())
602 SdrObject* pObj1 = aIter.Next();
604 // all members of a group have to be convertible
605 if(!ImpCanConvertForCombine1(pObj1))
607 return false;
611 else
613 if(!ImpCanConvertForCombine1(pObj))
615 return false;
619 return true;
622 basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj)
624 basegfx::B2DPolyPolygon aRetval;
625 const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj );
627 if(pPath && !pObj->GetOutlinerParaObject())
629 aRetval = pPath->GetPathPoly();
631 else
633 SdrObjectUniquePtr pConvObj = pObj->ConvertToPolyObj(true/*bCombine*/, false);
635 if(pConvObj)
637 SdrObjList* pOL = pConvObj->GetSubList();
639 if(pOL)
641 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
643 while(aIter.IsMore())
645 SdrObject* pObj1 = aIter.Next();
646 pPath = dynamic_cast<SdrPathObj*>( pObj1 );
648 if(pPath)
650 aRetval.append(pPath->GetPathPoly());
654 else
656 pPath = dynamic_cast<SdrPathObj*>( pConvObj.get() );
658 if(pPath)
660 aRetval = pPath->GetPathPoly();
666 return aRetval;
669 basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj)
671 SdrObjList* pOL = pObj->GetSubList();
673 if(pOL && !pObj->Is3DObj())
675 basegfx::B2DPolyPolygon aRetval;
676 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
678 while(aIter.IsMore())
680 SdrObject* pObj1 = aIter.Next();
681 aRetval.append(ImpGetPolyPolygon1(pObj1));
684 return aRetval;
686 else
688 return ImpGetPolyPolygon1(pObj);
692 basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
694 const sal_uInt32 nPolyCount(rPolyPolygon.count());
696 if(0 == nPolyCount)
698 return basegfx::B2DPolygon();
700 else if(1 == nPolyCount)
702 return rPolyPolygon.getB2DPolygon(0);
704 else
706 basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0));
708 for(sal_uInt32 a(1); a < nPolyCount; a++)
710 basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
712 if(aRetval.count())
714 if(aCandidate.count())
716 const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0));
717 const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1));
718 const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0));
719 const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1));
721 const double fRACA(basegfx::B2DVector(aCA - aRA).getLength());
722 const double fRACB(basegfx::B2DVector(aCB - aRA).getLength());
723 const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength());
724 const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength());
726 const double fSmallestRA(std::min(fRACA, fRACB));
727 const double fSmallestRB(std::min(fRBCA, fRBCB));
729 if(fSmallestRA < fSmallestRB)
731 // flip result
732 aRetval.flip();
735 const double fSmallestCA(std::min(fRACA, fRBCA));
736 const double fSmallestCB(std::min(fRACB, fRBCB));
738 if(fSmallestCB < fSmallestCA)
740 // flip candidate
741 aCandidate.flip();
744 // append candidate to retval
745 aRetval.append(aCandidate);
748 else
750 aRetval = aCandidate;
754 return aRetval;
758 namespace {
760 // for distribution dialog function
761 struct ImpDistributeEntry
763 SdrObject* mpObj;
764 sal_Int32 mnPos;
765 sal_Int32 mnLength;
770 typedef std::vector<ImpDistributeEntry> ImpDistributeEntryList;
772 void SdrEditView::DistributeMarkedObjects(sal_uInt16 SlotID)
774 const size_t nMark(GetMarkedObjectCount());
776 if(nMark <= 2)
777 return;
779 SvxDistributeHorizontal eHor = SvxDistributeHorizontal::NONE;
780 SvxDistributeVertical eVer = SvxDistributeVertical::NONE;
782 switch (SlotID)
784 case SID_DISTRIBUTE_HLEFT: eHor = SvxDistributeHorizontal::Left; break;
785 case SID_DISTRIBUTE_HCENTER: eHor = SvxDistributeHorizontal::Center; break;
786 case SID_DISTRIBUTE_HDISTANCE: eHor = SvxDistributeHorizontal::Distance; break;
787 case SID_DISTRIBUTE_HRIGHT: eHor = SvxDistributeHorizontal::Right; break;
788 case SID_DISTRIBUTE_VTOP: eVer = SvxDistributeVertical::Top; break;
789 case SID_DISTRIBUTE_VCENTER: eVer = SvxDistributeVertical::Center; break;
790 case SID_DISTRIBUTE_VDISTANCE: eVer = SvxDistributeVertical::Distance; break;
791 case SID_DISTRIBUTE_VBOTTOM: eVer = SvxDistributeVertical::Bottom; break;
794 ImpDistributeEntryList aEntryList;
795 ImpDistributeEntryList::iterator itEntryList;
796 sal_uInt32 nFullLength;
798 const bool bUndo = IsUndoEnabled();
799 if( bUndo )
800 BegUndo();
802 if(eHor != SvxDistributeHorizontal::NONE)
804 // build sorted entry list
805 nFullLength = 0;
807 for( size_t a = 0; a < nMark; ++a )
809 SdrMark* pMark = GetSdrMarkByIndex(a);
810 ImpDistributeEntry aNew;
812 aNew.mpObj = pMark->GetMarkedSdrObj();
814 switch(eHor)
816 case SvxDistributeHorizontal::Left:
818 aNew.mnPos = aNew.mpObj->GetSnapRect().Left();
819 break;
821 case SvxDistributeHorizontal::Center:
823 aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
824 break;
826 case SvxDistributeHorizontal::Distance:
828 aNew.mnLength = aNew.mpObj->GetSnapRect().GetWidth() + 1;
829 nFullLength += aNew.mnLength;
830 aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
831 break;
833 case SvxDistributeHorizontal::Right:
835 aNew.mnPos = aNew.mpObj->GetSnapRect().Right();
836 break;
838 default: break;
841 itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
842 [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
843 if ( itEntryList < aEntryList.end() )
844 aEntryList.insert( itEntryList, aNew );
845 else
846 aEntryList.push_back( aNew );
849 if(eHor == SvxDistributeHorizontal::Distance)
851 // calculate room in-between
852 sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1;
853 double fStepWidth = (static_cast<double>(nWidth) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
854 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
855 fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
857 // move entries 1..n-1
858 for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i )
860 ImpDistributeEntry& rCurr = aEntryList[ i ];
861 ImpDistributeEntry& rNext = aEntryList[ i + 1];
862 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
863 if( bUndo )
864 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
865 rCurr.mpObj->Move(Size(nDelta, 0));
866 fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
869 else
871 // calculate distances
872 sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
873 double fStepWidth = static_cast<double>(nWidth) / static_cast<double>(aEntryList.size() - 1);
874 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
875 fStepStart += fStepWidth;
877 // move entries 1..n-1
878 for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i )
880 ImpDistributeEntry& rCurr = aEntryList[ i ];
881 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
882 if( bUndo )
883 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
884 rCurr.mpObj->Move(Size(nDelta, 0));
885 fStepStart += fStepWidth;
889 // clear list
890 aEntryList.clear();
893 if(eVer != SvxDistributeVertical::NONE)
895 // build sorted entry list
896 nFullLength = 0;
898 for( size_t a = 0; a < nMark; ++a )
900 SdrMark* pMark = GetSdrMarkByIndex(a);
901 ImpDistributeEntry aNew;
903 aNew.mpObj = pMark->GetMarkedSdrObj();
905 switch(eVer)
907 case SvxDistributeVertical::Top:
909 aNew.mnPos = aNew.mpObj->GetSnapRect().Top();
910 break;
912 case SvxDistributeVertical::Center:
914 aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
915 break;
917 case SvxDistributeVertical::Distance:
919 aNew.mnLength = aNew.mpObj->GetSnapRect().GetHeight() + 1;
920 nFullLength += aNew.mnLength;
921 aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
922 break;
924 case SvxDistributeVertical::Bottom:
926 aNew.mnPos = aNew.mpObj->GetSnapRect().Bottom();
927 break;
929 default: break;
932 itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
933 [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
934 if ( itEntryList < aEntryList.end() )
935 aEntryList.insert( itEntryList, aNew );
936 else
937 aEntryList.push_back( aNew );
940 if(eVer == SvxDistributeVertical::Distance)
942 // calculate room in-between
943 sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1;
944 double fStepWidth = (static_cast<double>(nHeight) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
945 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
946 fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
948 // move entries 1..n-1
949 for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
951 ImpDistributeEntry& rCurr = aEntryList[ i ];
952 ImpDistributeEntry& rNext = aEntryList[ i + 1 ];
953 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
954 if( bUndo )
955 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
956 rCurr.mpObj->Move(Size(0, nDelta));
957 fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
960 else
962 // calculate distances
963 sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
964 double fStepWidth = static_cast<double>(nHeight) / static_cast<double>(aEntryList.size() - 1);
965 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
966 fStepStart += fStepWidth;
968 // move entries 1..n-1
969 for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
971 ImpDistributeEntry& rCurr = aEntryList[ i ];
972 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
973 if( bUndo )
974 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
975 rCurr.mpObj->Move(Size(0, nDelta));
976 fStepStart += fStepWidth;
980 // clear list
981 aEntryList.clear();
984 // UNDO-Comment and end of UNDO
985 mpModel->SetUndoComment(SvxResId(STR_DistributeMarkedObjects));
987 if( bUndo )
988 EndUndo();
991 void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode)
993 // #i73441# check content
994 if(!AreObjectsMarked())
995 return;
997 SdrMarkList aRemove;
998 SortMarkedObjects();
1000 const bool bUndo = IsUndoEnabled();
1002 if( bUndo )
1003 BegUndo();
1005 size_t nInsPos = SAL_MAX_SIZE;
1006 const SdrObject* pAttrObj = nullptr;
1007 basegfx::B2DPolyPolygon aMergePolyPolygonA;
1008 basegfx::B2DPolyPolygon aMergePolyPolygonB;
1010 SdrObjList* pInsOL = nullptr;
1011 SdrPageView* pInsPV = nullptr;
1012 bool bFirstObjectComplete(false);
1014 // make sure selected objects are contour objects
1015 // since now basegfx::utils::adaptiveSubdivide() is used, it is no longer
1016 // necessary to use ConvertMarkedToPolyObj which will subdivide curves using the old
1017 // mechanisms. In a next step the polygon clipper will even be able to clip curves...
1018 // ConvertMarkedToPolyObj(true);
1019 ConvertMarkedToPathObj(true);
1020 OSL_ENSURE(AreObjectsMarked(), "no more objects selected after preparations (!)");
1022 for(size_t a=0; a<GetMarkedObjectCount(); ++a)
1024 SdrMark* pM = GetSdrMarkByIndex(a);
1025 SdrObject* pObj = pM->GetMarkedSdrObj();
1027 if(ImpCanConvertForCombine(pObj))
1029 if(!pAttrObj)
1030 pAttrObj = pObj;
1032 nInsPos = pObj->GetOrdNum() + 1;
1033 pInsPV = pM->GetPageView();
1034 pInsOL = pObj->getParentSdrObjListFromSdrObject();
1036 // #i76891# use single iteration from SJ here which works on SdrObjects and takes
1037 // groups into account by itself
1038 SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
1040 while(aIter.IsMore())
1042 SdrObject* pCandidate = aIter.Next();
1043 SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>( pCandidate );
1044 if(pPathObj)
1046 basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly());
1048 // #i76891# unfortunately ConvertMarkedToPathObj has converted all
1049 // involved polygon data to curve segments, even if not necessary.
1050 // It is better to try to reduce to more simple polygons.
1051 aTmpPoly = basegfx::utils::simplifyCurveSegments(aTmpPoly);
1053 // for each part polygon as preparation, remove self-intersections
1054 // correct orientations and get rid of possible neutral polygons.
1055 aTmpPoly = basegfx::utils::prepareForPolygonOperation(aTmpPoly);
1057 if(!bFirstObjectComplete)
1059 // #i111987# Also need to collect ORed source shape when more than
1060 // a single polygon is involved
1061 if(aMergePolyPolygonA.count())
1063 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly);
1065 else
1067 aMergePolyPolygonA = aTmpPoly;
1070 else
1072 if(aMergePolyPolygonB.count())
1074 // to topologically correctly collect the 2nd polygon
1075 // group it is necessary to OR the parts (each is seen as
1076 // XOR-FillRule polygon and they are drawn over each-other)
1077 aMergePolyPolygonB = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly);
1079 else
1081 aMergePolyPolygonB = aTmpPoly;
1087 // was there something added to the first polygon?
1088 if(!bFirstObjectComplete && aMergePolyPolygonA.count())
1090 bFirstObjectComplete = true;
1093 // move object to temporary delete list
1094 aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView()));
1098 switch(eMode)
1100 case SdrMergeMode::Merge:
1102 // merge all contained parts (OR)
1103 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
1104 break;
1106 case SdrMergeMode::Subtract:
1108 // Subtract B from A
1109 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB);
1110 break;
1112 case SdrMergeMode::Intersect:
1114 // AND B and A
1115 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
1116 break;
1120 // #i73441# check insert list before taking actions
1121 if(pInsOL)
1123 SdrPathObj* pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), OBJ_PATHFILL, aMergePolyPolygonA);
1124 ImpCopyAttributes(pAttrObj, pPath);
1125 pInsOL->InsertObject(pPath, nInsPos);
1126 if( bUndo )
1127 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
1129 // #i124760# To have a correct selection with only the new object it is necessary to
1130 // unmark all objects first. If not doing so, there may remain invalid pointers to objects
1131 // TTTT:Not needed for aw080 (!)
1132 UnmarkAllObj(pInsPV);
1134 MarkObj(pPath, pInsPV, false, true);
1137 aRemove.ForceSort();
1138 switch(eMode)
1140 case SdrMergeMode::Merge:
1142 SetUndoComment(
1143 SvxResId(STR_EditMergeMergePoly),
1144 aRemove.GetMarkDescription());
1145 break;
1147 case SdrMergeMode::Subtract:
1149 SetUndoComment(
1150 SvxResId(STR_EditMergeSubstractPoly),
1151 aRemove.GetMarkDescription());
1152 break;
1154 case SdrMergeMode::Intersect:
1156 SetUndoComment(
1157 SvxResId(STR_EditMergeIntersectPoly),
1158 aRemove.GetMarkDescription());
1159 break;
1162 DeleteMarkedList(aRemove);
1164 if( bUndo )
1165 EndUndo();
1168 void SdrEditView::EqualizeMarkedObjects(bool bWidth)
1170 const SdrMarkList& rMarkList = GetMarkedObjectList();
1171 size_t nMarked = rMarkList.GetMarkCount();
1173 if (nMarked < 2)
1174 return;
1176 size_t nLastSelected = 0;
1177 sal_Int64 nLastSelectedTime = rMarkList.GetMark(0)->getTimeStamp();
1178 for (size_t a = 1; a < nMarked; ++a)
1180 sal_Int64 nCandidateTime = rMarkList.GetMark(a)->getTimeStamp();
1181 if (nCandidateTime > nLastSelectedTime)
1183 nLastSelectedTime = nCandidateTime;
1184 nLastSelected = a;
1188 SdrObject* pLastSelectedObj = rMarkList.GetMark(nLastSelected)->GetMarkedSdrObj();
1189 Size aLastRectSize(pLastSelectedObj->GetLogicRect().GetSize());
1191 const bool bUndo = IsUndoEnabled();
1193 if (bUndo)
1194 BegUndo();
1196 for (size_t a = 0; a < nMarked; ++a)
1198 if (a == nLastSelected)
1199 continue;
1200 SdrMark* pM = rMarkList.GetMark(a);
1201 SdrObject* pObj = pM->GetMarkedSdrObj();
1202 tools::Rectangle aLogicRect(pObj->GetLogicRect());
1203 Size aLogicRectSize(aLogicRect.GetSize());
1204 if (bWidth)
1205 aLogicRectSize.setWidth( aLastRectSize.Width() );
1206 else
1207 aLogicRectSize.setHeight( aLastRectSize.Height() );
1208 aLogicRect.SetSize(aLogicRectSize);
1209 if (bUndo)
1210 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
1211 pObj->SetLogicRect(aLogicRect);
1214 SetUndoComment(
1215 SvxResId(bWidth ? STR_EqualizeWidthMarkedObjects : STR_EqualizeHeightMarkedObjects),
1216 rMarkList.GetMarkDescription());
1218 if (bUndo)
1219 EndUndo();
1222 void SdrEditView::CombineMarkedTextObjects()
1224 SdrPageView* pPageView = GetSdrPageView();
1225 if ( !pPageView || pPageView->IsLayerLocked( GetActiveLayer() ) )
1226 return;
1228 bool bUndo = IsUndoEnabled();
1230 // Undo-String will be set later
1231 if ( bUndo )
1232 BegUndo();
1234 SdrOutliner& rDrawOutliner = getSdrModelFromSdrView().GetDrawOutliner();
1236 SdrObjListIter aIter( GetMarkedObjectList(), SdrIterMode::Flat);
1237 while ( aIter.IsMore() )
1239 SdrObject* pObj = aIter.Next();
1240 SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pObj );
1241 const OutlinerParaObject* pOPO = pTextObj ? pTextObj->GetOutlinerParaObject() : nullptr;
1242 if ( pOPO && pTextObj->IsTextFrame()
1243 && pTextObj->GetObjIdentifier() == OBJ_TEXT // not callouts (OBJ_CAPTION)
1244 && !pTextObj->IsOutlText() // not impress presentation objects
1245 && pTextObj->GetMergedItem(XATTR_FORMTXTSTYLE).GetValue() == XFormTextStyle::NONE // not Fontwork
1248 // if the last paragraph does not end in paragraph-end punctuation (ignoring whitespace),
1249 // assume this text should be added to the end of the last paragraph, instead of starting a new paragraph.
1250 const sal_Int32 nPara = rDrawOutliner.GetParagraphCount();
1251 const OUString sLastPara = nPara ? rDrawOutliner.GetText( rDrawOutliner.GetParagraph( nPara - 1 ) ) : "";
1252 sal_Int32 n = sLastPara.getLength();
1253 while ( n && unicode::isWhiteSpace( sLastPara[--n] ) )
1255 //TODO: find way to use Locale to identify sentence final punctuation. Copied IsSentenceAtEnd() from autofmt.cxx
1256 const bool bAppend = !n || ( sLastPara[n] != '.' && sLastPara[n] != '?' && sLastPara[n] != '!' );
1257 rDrawOutliner.AddText( *pOPO, bAppend );
1259 else
1261 // Unmark non-textboxes, because all marked objects are deleted at the end. AdjustMarkHdl later.
1262 MarkObj(pObj, pPageView, /*bUnmark=*/true, /*bImpNoSetMarkHdl=*/true);
1266 MarkListHasChanged();
1267 AdjustMarkHdl();
1269 if ( GetMarkedObjectCount() > 1 )
1271 SdrRectObj* pReplacement = new SdrRectObj( getSdrModelFromSdrView(), OBJ_TEXT );
1272 pReplacement->SetOutlinerParaObject( rDrawOutliner.CreateParaObject() );
1273 pReplacement->SetSnapRect( GetMarkedObjRect() );
1275 const SdrInsertFlags nFlags = SdrInsertFlags::DONTMARK | SdrInsertFlags::SETDEFLAYER;
1276 if ( InsertObjectAtView( pReplacement, *pPageView, nFlags ) )
1277 DeleteMarkedObj();
1280 if ( bUndo )
1281 EndUndo();
1283 return;
1286 void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly)
1288 // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would
1289 // create a 2nd Undo-action and Undo-Comment.
1291 bool bUndo = IsUndoEnabled();
1293 // Undo-String will be set later
1294 if( bUndo )
1295 BegUndo("", "", bNoPolyPoly ? SdrRepeatFunc::CombineOnePoly : SdrRepeatFunc::CombinePolyPoly);
1297 // #105899# First, guarantee that all objects are converted to polyobjects,
1298 // especially for SdrGrafObj with bitmap filling this is necessary to not
1299 // lose the bitmap filling.
1301 // #i12392#
1302 // ConvertMarkedToPolyObj was too strong here, it will lose quality and
1303 // information when curve objects are combined. This can be replaced by
1304 // using ConvertMarkedToPathObj without changing the previous fix.
1306 // #i21250#
1307 // Instead of simply passing true as LineToArea, use bNoPolyPoly as info
1308 // if this command is a 'Combine' or a 'Connect' command. On Connect it's true.
1309 // To not concert line segments with a set line width to polygons in that case,
1310 // use this info. Do not convert LineToArea on Connect commands.
1311 // ConvertMarkedToPathObj(!bNoPolyPoly);
1313 // This is used for Combine and Connect. In no case it is necessary to force
1314 // the content to curve, but it is also not good to force to polygons. Thus,
1315 // curve is the less information losing one. Remember: This place is not
1316 // used for merge.
1317 // LineToArea is never necessary, both commands are able to take over the
1318 // set line style and to display it correctly. Thus, i will use a
1319 // ConvertMarkedToPathObj with a false in any case. Only drawback is that
1320 // simple polygons will be changed to curves, but with no information loss.
1321 ConvertMarkedToPathObj(false /* bLineToArea */);
1323 // continue as before
1324 basegfx::B2DPolyPolygon aPolyPolygon;
1325 SdrObjList* pCurrentOL = nullptr;
1326 SdrMarkList aRemoveBuffer;
1328 SortMarkedObjects();
1329 size_t nInsPos = SAL_MAX_SIZE;
1330 SdrObjList* pInsOL = nullptr;
1331 SdrPageView* pInsPV = nullptr;
1332 const SdrObject* pAttrObj = nullptr;
1334 for(size_t a = GetMarkedObjectCount(); a; )
1336 --a;
1337 SdrMark* pM = GetSdrMarkByIndex(a);
1338 SdrObject* pObj = pM->GetMarkedSdrObj();
1339 SdrObjList* pThisOL = pObj->getParentSdrObjListFromSdrObject();
1341 if(pCurrentOL != pThisOL)
1343 pCurrentOL = pThisOL;
1346 if(ImpCanConvertForCombine(pObj))
1348 // remember objects to be able to copy attributes
1349 pAttrObj = pObj;
1351 // unfortunately ConvertMarkedToPathObj has converted all
1352 // involved polygon data to curve segments, even if not necessary.
1353 // It is better to try to reduce to more simple polygons.
1354 basegfx::B2DPolyPolygon aTmpPoly(basegfx::utils::simplifyCurveSegments(ImpGetPolyPolygon(pObj)));
1355 aPolyPolygon.insert(0, aTmpPoly);
1357 if(!pInsOL)
1359 nInsPos = pObj->GetOrdNum() + 1;
1360 pInsPV = pM->GetPageView();
1361 pInsOL = pObj->getParentSdrObjListFromSdrObject();
1364 aRemoveBuffer.InsertEntry(SdrMark(pObj, pM->GetPageView()));
1368 if(bNoPolyPoly)
1370 basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon));
1371 aPolyPolygon.clear();
1372 aPolyPolygon.append(aCombinedPolygon);
1375 const sal_uInt32 nPolyCount(aPolyPolygon.count());
1377 if (nPolyCount && pAttrObj)
1379 SdrObjKind eKind = OBJ_PATHFILL;
1381 if(nPolyCount > 1)
1383 aPolyPolygon.setClosed(true);
1385 else
1387 // check for Polyline
1388 const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1389 const sal_uInt32 nPointCount(aPolygon.count());
1391 if(nPointCount <= 2)
1393 eKind = OBJ_PATHLINE;
1395 else
1397 if(!aPolygon.isClosed())
1399 const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0));
1400 const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1));
1401 const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength());
1402 const double fJoinTolerance(10.0);
1404 if(fDistance < fJoinTolerance)
1406 aPolyPolygon.setClosed(true);
1408 else
1410 eKind = OBJ_PATHLINE;
1416 SdrPathObj* pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), eKind, aPolyPolygon);
1418 // attributes of the lowest object
1419 ImpCopyAttributes(pAttrObj, pPath);
1421 // If LineStyle of pAttrObj is drawing::LineStyle_NONE force to drawing::LineStyle_SOLID to make visible.
1422 const drawing::LineStyle eLineStyle = pAttrObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
1423 const drawing::FillStyle eFillStyle = pAttrObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
1425 // Take fill style/closed state of pAttrObj in account when deciding to change the line style
1426 bool bIsClosedPathObj(dynamic_cast<const SdrPathObj*>( pAttrObj) != nullptr && static_cast<const SdrPathObj*>(pAttrObj)->IsClosed());
1428 if(drawing::LineStyle_NONE == eLineStyle && (drawing::FillStyle_NONE == eFillStyle || !bIsClosedPathObj))
1430 pPath->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID));
1433 pInsOL->InsertObject(pPath,nInsPos);
1434 if( bUndo )
1435 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
1437 // Here was a severe error: Without UnmarkAllObj, the new object was marked
1438 // additionally to the two ones which are deleted below. As long as those are
1439 // in the UNDO there is no problem, but as soon as they get deleted, the
1440 // MarkList will contain deleted objects -> GPF.
1441 UnmarkAllObj(pInsPV);
1442 MarkObj(pPath, pInsPV, false, true);
1445 // build an UndoComment from the objects actually used
1446 aRemoveBuffer.ForceSort(); // important for remove (see below)
1447 if( bUndo )
1448 SetUndoComment(SvxResId(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveBuffer.GetMarkDescription());
1450 // remove objects actually used from the list
1451 DeleteMarkedList(aRemoveBuffer);
1452 if( bUndo )
1453 EndUndo();
1457 // Dismantle
1460 bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, bool bMakeLines)
1462 bool bCan(false);
1463 const sal_uInt32 nPolygonCount(rPpolyPolygon.count());
1465 if(nPolygonCount >= 2)
1467 // #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon
1468 bCan = true;
1470 else if(bMakeLines && 1 == nPolygonCount)
1472 // #i69172# ..or with at least 2 edges (curves or lines)
1473 const basegfx::B2DPolygon& aPolygon(rPpolyPolygon.getB2DPolygon(0));
1474 const sal_uInt32 nPointCount(aPolygon.count());
1476 if(nPointCount > 2)
1478 bCan = true;
1482 return bCan;
1485 bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, bool bMakeLines)
1487 bool bOtherObjs(false); // true=objects other than PathObj's existent
1488 bool bMin1PolyPoly(false); // true=at least 1 tools::PolyPolygon with more than one Polygon existent
1489 SdrObjList* pOL = pObj->GetSubList();
1491 if(pOL)
1493 // group object -- check all members if they're PathObjs
1494 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
1496 while(aIter.IsMore() && !bOtherObjs)
1498 const SdrObject* pObj1 = aIter.Next();
1499 const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj1 );
1501 if(pPath)
1503 if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines))
1505 bMin1PolyPoly = true;
1508 SdrObjTransformInfoRec aInfo;
1509 pObj1->TakeObjInfo(aInfo);
1511 if(!aInfo.bCanConvToPath)
1513 // happens e. g. in the case of FontWork
1514 bOtherObjs = true;
1517 else
1519 bOtherObjs = true;
1523 else
1525 const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>(pObj);
1526 const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>(pObj);
1528 // #i37011#
1529 if(pPath)
1531 if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines))
1533 bMin1PolyPoly = true;
1536 SdrObjTransformInfoRec aInfo;
1537 pObj->TakeObjInfo(aInfo);
1539 // new condition IsLine() to be able to break simple Lines
1540 if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine())
1542 // happens e. g. in the case of FontWork
1543 bOtherObjs = true;
1546 else if(pCustomShape)
1548 if(bMakeLines)
1550 // allow break command
1551 bMin1PolyPoly = true;
1554 else
1556 bOtherObjs = true;
1559 return bMin1PolyPoly && !bOtherObjs;
1562 void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, size_t& rPos, SdrPageView* pPV, bool bMakeLines)
1564 const SdrPathObj* pSrcPath = dynamic_cast<const SdrPathObj*>( pObj );
1565 const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>( pObj );
1567 const bool bUndo = IsUndoEnabled();
1569 if(pSrcPath)
1571 // #i74631# redesigned due to XpolyPolygon removal and explicit constructors
1572 SdrObject* pLast = nullptr; // to be able to apply OutlinerParaObject
1573 const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly());
1574 const sal_uInt32 nPolyCount(rPolyPolygon.count());
1576 for(sal_uInt32 a(0); a < nPolyCount; a++)
1578 const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a));
1579 const sal_uInt32 nPointCount(rCandidate.count());
1581 if(!bMakeLines || nPointCount < 2)
1583 SdrPathObj* pPath = new SdrPathObj(
1584 pSrcPath->getSdrModelFromSdrObject(),
1585 pSrcPath->GetObjIdentifier(),
1586 basegfx::B2DPolyPolygon(rCandidate));
1587 ImpCopyAttributes(pSrcPath, pPath);
1588 pLast = pPath;
1589 rOL.InsertObject(pPath, rPos);
1590 if( bUndo )
1591 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
1592 MarkObj(pPath, pPV, false, true);
1593 rPos++;
1595 else
1597 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
1599 for(sal_uInt32 b(0); b < nLoopCount; b++)
1601 SdrObjKind eKind(OBJ_PLIN);
1602 basegfx::B2DPolygon aNewPolygon;
1603 const sal_uInt32 nNextIndex((b + 1) % nPointCount);
1605 aNewPolygon.append(rCandidate.getB2DPoint(b));
1607 if(rCandidate.areControlPointsUsed())
1609 aNewPolygon.appendBezierSegment(
1610 rCandidate.getNextControlPoint(b),
1611 rCandidate.getPrevControlPoint(nNextIndex),
1612 rCandidate.getB2DPoint(nNextIndex));
1613 eKind = OBJ_PATHLINE;
1615 else
1617 aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex));
1620 SdrPathObj* pPath = new SdrPathObj(
1621 pSrcPath->getSdrModelFromSdrObject(),
1622 eKind,
1623 basegfx::B2DPolyPolygon(aNewPolygon));
1624 ImpCopyAttributes(pSrcPath, pPath);
1625 pLast = pPath;
1626 rOL.InsertObject(pPath, rPos);
1627 if( bUndo )
1628 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
1629 MarkObj(pPath, pPV, false, true);
1630 rPos++;
1635 if(pLast && pSrcPath->GetOutlinerParaObject())
1637 pLast->SetOutlinerParaObject(std::make_unique<OutlinerParaObject>(*pSrcPath->GetOutlinerParaObject()));
1640 else if(pCustomShape)
1642 if(bMakeLines)
1644 // break up custom shape
1645 const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape();
1647 if(pReplacement)
1649 SdrObject* pCandidate(pReplacement->CloneSdrObject(pReplacement->getSdrModelFromSdrObject()));
1650 DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)");
1652 if(pCustomShape->GetMergedItem(SDRATTR_SHADOW).GetValue())
1654 if(dynamic_cast<const SdrObjGroup*>( pReplacement) != nullptr)
1656 pCandidate->SetMergedItem(makeSdrShadowItem(true));
1660 rOL.InsertObject(pCandidate, rPos);
1661 if( bUndo )
1662 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true));
1663 MarkObj(pCandidate, pPV, false, true);
1665 if(pCustomShape->HasText() && !pCustomShape->IsTextPath())
1667 // #i37011# also create a text object and add at rPos + 1
1668 SdrObject* pTextObj = SdrObjFactory::MakeNewObject(
1669 pCustomShape->getSdrModelFromSdrObject(),
1670 pCustomShape->GetObjInventor(),
1671 OBJ_TEXT);
1673 // Copy text content
1674 OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject();
1675 if(pParaObj)
1677 pTextObj->NbcSetOutlinerParaObject(std::make_unique<OutlinerParaObject>(*pParaObj));
1680 // copy all attributes
1681 SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet());
1683 // clear fill and line style
1684 aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
1685 aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
1687 // get the text bounds and set at text object
1688 tools::Rectangle aTextBounds = pCustomShape->GetSnapRect();
1689 if(pCustomShape->GetTextBounds(aTextBounds))
1691 pTextObj->SetSnapRect(aTextBounds);
1694 // if rotated, copy GeoStat, too.
1695 const GeoStat& rSourceGeo = pCustomShape->GetGeoStat();
1696 if(rSourceGeo.nRotationAngle)
1698 pTextObj->NbcRotate(
1699 pCustomShape->GetSnapRect().Center(), rSourceGeo.nRotationAngle,
1700 rSourceGeo.nSin, rSourceGeo.nCos);
1703 // set modified ItemSet at text object
1704 pTextObj->SetMergedItemSet(aTargetItemSet);
1706 // insert object
1707 rOL.InsertObject(pTextObj, rPos + 1);
1708 if( bUndo )
1709 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true));
1710 MarkObj(pTextObj, pPV, false, true);
1717 void SdrEditView::DismantleMarkedObjects(bool bMakeLines)
1719 // temporary MarkList
1720 SdrMarkList aRemoveBuffer;
1722 SortMarkedObjects();
1724 const bool bUndo = IsUndoEnabled();
1726 if( bUndo )
1728 // comment is constructed later
1729 BegUndo("", "", bMakeLines ? SdrRepeatFunc::DismantleLines : SdrRepeatFunc::DismantlePolys);
1732 SdrObjList* pOL0=nullptr;
1733 for (size_t nm=GetMarkedObjectCount(); nm>0;) {
1734 --nm;
1735 SdrMark* pM=GetSdrMarkByIndex(nm);
1736 SdrObject* pObj=pM->GetMarkedSdrObj();
1737 SdrPageView* pPV=pM->GetPageView();
1738 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
1739 if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // make sure OrdNums are correct!
1740 if (ImpCanDismantle(pObj,bMakeLines)) {
1741 aRemoveBuffer.InsertEntry(SdrMark(pObj,pM->GetPageView()));
1742 const size_t nPos0=pObj->GetOrdNumDirect();
1743 size_t nPos=nPos0+1;
1744 SdrObjList* pSubList=pObj->GetSubList();
1745 if (pSubList!=nullptr && !pObj->Is3DObj()) {
1746 SdrObjListIter aIter(pSubList,SdrIterMode::DeepNoGroups);
1747 while (aIter.IsMore()) {
1748 const SdrObject* pObj1=aIter.Next();
1749 ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines);
1751 } else {
1752 ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines);
1754 if( bUndo )
1755 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,true));
1756 pOL->RemoveObject(nPos0);
1758 if( !bUndo )
1759 SdrObject::Free(pObj);
1763 if( bUndo )
1765 // construct UndoComment from objects actually used
1766 SetUndoComment(SvxResId(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveBuffer.GetMarkDescription());
1767 // remove objects actually used from the list
1768 EndUndo();
1773 // Group
1776 void SdrEditView::GroupMarked()
1778 if (!AreObjectsMarked())
1779 return;
1781 SortMarkedObjects();
1783 const bool bUndo = IsUndoEnabled();
1784 if( bUndo )
1786 BegUndo(SvxResId(STR_EditGroup),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Group);
1788 for(size_t nm = GetMarkedObjectCount(); nm>0; )
1790 // add UndoActions for all affected objects
1791 --nm;
1792 SdrMark* pM=GetSdrMarkByIndex(nm);
1793 SdrObject* pObj = pM->GetMarkedSdrObj();
1794 AddUndoActions( CreateConnectorUndo( *pObj ) );
1795 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject( *pObj ));
1799 SdrMarkList aNewMark;
1800 SdrPageView* pPV = GetSdrPageView();
1802 if(pPV)
1804 SdrObjList* pCurrentLst=pPV->GetObjList();
1805 SdrObjList* pSrcLst=pCurrentLst;
1806 SdrObjList* pSrcLst0=pSrcLst;
1807 // make sure OrdNums are correct
1808 if (pSrcLst->IsObjOrdNumsDirty())
1809 pSrcLst->RecalcObjOrdNums();
1810 SdrObject* pGrp=nullptr;
1811 SdrObjList* pDstLst=nullptr;
1812 // if all selected objects come from foreign object lists.
1813 // the group object is the last one in the list.
1814 size_t nInsPos=pSrcLst->GetObjCount();
1815 bool bNeedInsPos=true;
1816 for (size_t nm=GetMarkedObjectCount(); nm>0;)
1818 --nm;
1819 SdrMark* pM=GetSdrMarkByIndex(nm);
1820 if (pM->GetPageView()==pPV)
1822 SdrObject* pObj=pM->GetMarkedSdrObj();
1823 if (nullptr==pGrp)
1825 pGrp = new SdrObjGroup(pObj->getSdrModelFromSdrObject());
1826 pDstLst=pGrp->GetSubList();
1827 DBG_ASSERT(pDstLst!=nullptr,"Alleged group object doesn't return object list.");
1829 pSrcLst=pObj->getParentSdrObjListFromSdrObject();
1830 if (pSrcLst!=pSrcLst0)
1832 if (pSrcLst->IsObjOrdNumsDirty())
1833 pSrcLst->RecalcObjOrdNums();
1835 bool bForeignList=pSrcLst!=pCurrentLst;
1836 if (!bForeignList && bNeedInsPos)
1838 nInsPos=pObj->GetOrdNum(); // this way, all ObjOrdNum of the page are set
1839 nInsPos++;
1840 bNeedInsPos=false;
1842 pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
1843 if (!bForeignList)
1844 nInsPos--; // correct InsertPos
1845 pDstLst->InsertObject(pObj,0);
1846 GetMarkedObjectListWriteAccess().DeleteMark(nm);
1847 pSrcLst0=pSrcLst;
1850 if (pGrp!=nullptr)
1852 aNewMark.InsertEntry(SdrMark(pGrp,pPV));
1853 const size_t nCount=pDstLst->GetObjCount();
1854 pCurrentLst->InsertObject(pGrp,nInsPos);
1855 if( bUndo )
1857 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // no recalculation!
1858 for (size_t no=0; no<nCount; ++no)
1860 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
1865 GetMarkedObjectListWriteAccess().Merge(aNewMark);
1866 MarkListHasChanged();
1868 if( bUndo )
1869 EndUndo();
1873 // Ungroup
1876 void SdrEditView::UnGroupMarked()
1878 SdrMarkList aNewMark;
1880 const bool bUndo = IsUndoEnabled();
1881 if( bUndo )
1882 BegUndo("", "", SdrRepeatFunc::Ungroup);
1884 size_t nCount=0;
1885 OUString aName1;
1886 OUString aName;
1887 bool bNameOk=false;
1888 for (size_t nm=GetMarkedObjectCount(); nm>0;) {
1889 --nm;
1890 SdrMark* pM=GetSdrMarkByIndex(nm);
1891 SdrObject* pGrp=pM->GetMarkedSdrObj();
1892 SdrObjList* pSrcLst=pGrp->GetSubList();
1893 if (pSrcLst!=nullptr) {
1894 nCount++;
1895 if (nCount==1) {
1896 aName = pGrp->TakeObjNameSingul(); // retrieve name of group
1897 aName1 = pGrp->TakeObjNamePlural(); // retrieve name of group
1898 bNameOk=true;
1899 } else {
1900 if (nCount==2) aName=aName1; // set plural name
1901 if (bNameOk) {
1902 OUString aStr(pGrp->TakeObjNamePlural()); // retrieve name of group
1904 if (aStr != aName)
1905 bNameOk = false;
1908 size_t nDstCnt=pGrp->GetOrdNum();
1909 SdrObjList* pDstLst=pM->GetPageView()->GetObjList();
1911 // FIRST move contained objects to parent of group, so that
1912 // the contained objects are NOT migrated to the UNDO-ItemPool
1913 // when AddUndo(new SdrUndoDelObj(*pGrp)) is called.
1914 const size_t nObjCount=pSrcLst->GetObjCount();
1916 if( bUndo )
1918 for (size_t no=nObjCount; no>0;)
1920 no--;
1921 SdrObject* pObj=pSrcLst->GetObj(no);
1922 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject(*pObj));
1925 for (size_t no=0; no<nObjCount; ++no)
1927 SdrObject* pObj=pSrcLst->RemoveObject(0);
1928 pDstLst->InsertObject(pObj,nDstCnt);
1929 if( bUndo )
1930 AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true));
1931 nDstCnt++;
1932 // No SortCheck when inserting into MarkList, because that would
1933 // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum():
1934 aNewMark.InsertEntry(SdrMark(pObj,pM->GetPageView()),false);
1937 if( bUndo )
1939 // Now it is safe to add the delete-UNDO which triggers the
1940 // MigrateItemPool now only for itself, not for the sub-objects.
1941 // nDstCnt is right, because previous inserts move group
1942 // object deeper and increase nDstCnt.
1943 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp));
1945 pDstLst->RemoveObject(nDstCnt);
1947 if( !bUndo )
1948 SdrObject::Free(pGrp);
1950 GetMarkedObjectListWriteAccess().DeleteMark(nm);
1953 if (nCount!=0)
1955 if (!bNameOk)
1956 aName=SvxResId(STR_ObjNamePluralGRUP); // Use the term "Group Objects," if different objects are grouped.
1957 SetUndoComment(SvxResId(STR_EditUngroup),aName);
1960 if( bUndo )
1961 EndUndo();
1963 if (nCount!=0)
1965 GetMarkedObjectListWriteAccess().Merge(aNewMark,true); // Because of the sorting above, aNewMark is reversed
1966 MarkListHasChanged();
1971 // ConvertToPoly
1974 SdrObject* SdrEditView::ImpConvertOneObj(SdrObject* pObj, bool bPath, bool bLineToArea)
1976 SdrObjectUniquePtr pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea);
1977 if (pNewObj)
1979 SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
1980 const bool bUndo = IsUndoEnabled();
1981 if( bUndo )
1982 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj));
1984 pOL->ReplaceObject(pNewObj.get(), pObj->GetOrdNum());
1986 if( !bUndo )
1987 SdrObject::Free(pObj);
1989 return pNewObj.release();
1992 void SdrEditView::ImpConvertTo(bool bPath, bool bLineToArea)
1994 if (!AreObjectsMarked()) return;
1996 bool bMrkChg = false;
1997 const size_t nMarkCount=GetMarkedObjectCount();
1998 const char* pDscrID = nullptr;
1999 if(bLineToArea)
2001 if(nMarkCount == 1)
2002 pDscrID = STR_EditConvToContour;
2003 else
2004 pDscrID = STR_EditConvToContours;
2006 BegUndo(SvxResId(pDscrID), GetDescriptionOfMarkedObjects());
2008 else
2010 if (bPath) {
2011 if (nMarkCount==1) pDscrID=STR_EditConvToCurve;
2012 else pDscrID=STR_EditConvToCurves;
2013 BegUndo(SvxResId(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPath);
2014 } else {
2015 if (nMarkCount==1) pDscrID=STR_EditConvToPoly;
2016 else pDscrID=STR_EditConvToPolys;
2017 BegUndo(SvxResId(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPoly);
2020 for (size_t nm=nMarkCount; nm>0;) {
2021 --nm;
2022 SdrMark* pM=GetSdrMarkByIndex(nm);
2023 SdrObject* pObj=pM->GetMarkedSdrObj();
2024 SdrPageView* pPV=pM->GetPageView();
2025 if (pObj->IsGroupObject() && !pObj->Is3DObj()) {
2026 SdrObject* pGrp=pObj;
2027 SdrObjListIter aIter(*pGrp, SdrIterMode::DeepNoGroups);
2028 while (aIter.IsMore()) {
2029 pObj=aIter.Next();
2030 ImpConvertOneObj(pObj,bPath,bLineToArea);
2032 } else {
2033 SdrObject* pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
2034 if (pNewObj!=nullptr) {
2035 bMrkChg=true;
2036 GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj,pPV),nm);
2040 EndUndo();
2041 if (bMrkChg)
2043 AdjustMarkHdl();
2044 MarkListHasChanged();
2048 void SdrEditView::ConvertMarkedToPathObj(bool bLineToArea)
2050 ImpConvertTo(true, bLineToArea);
2053 void SdrEditView::ConvertMarkedToPolyObj()
2055 ImpConvertTo(false, false/*bLineToArea*/);
2058 namespace
2060 GDIMetaFile GetMetaFile(SdrGrafObj const * pGraf)
2062 if (pGraf->HasGDIMetaFile())
2063 return pGraf->GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
2064 assert(pGraf->isEmbeddedVectorGraphicData());
2065 return pGraf->getMetafileFromEmbeddedVectorGraphicData();
2069 // Metafile Import
2070 void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo)
2072 const bool bUndo = IsUndoEnabled();
2074 if( bUndo )
2075 BegUndo("", "", SdrRepeatFunc::ImportMtf);
2077 SortMarkedObjects();
2078 SdrMarkList aForTheDescription;
2079 SdrMarkList aNewMarked;
2080 for (size_t nm =GetMarkedObjectCount(); nm > 0; )
2082 // create Undo objects for all new objects
2083 // check for cancellation between the metafiles
2084 if (pProgrInfo != nullptr)
2086 pProgrInfo->SetNextObject();
2087 if (!pProgrInfo->ReportActions(0))
2088 break;
2091 --nm;
2092 SdrMark* pM=GetSdrMarkByIndex(nm);
2093 SdrObject* pObj=pM->GetMarkedSdrObj();
2094 SdrPageView* pPV=pM->GetPageView();
2095 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
2096 const size_t nInsPos=pObj->GetOrdNum()+1;
2097 size_t nInsCnt=0;
2098 tools::Rectangle aLogicRect;
2100 SdrGrafObj* pGraf = dynamic_cast<SdrGrafObj*>( pObj );
2101 if (pGraf != nullptr)
2103 Graphic aGraphic = pGraf->GetGraphic();
2104 auto const & pVectorGraphicData = aGraphic.getVectorGraphicData();
2106 if (pVectorGraphicData && pVectorGraphicData->getVectorGraphicDataType() == VectorGraphicDataType::Pdf)
2108 #if HAVE_FEATURE_PDFIUM
2109 aLogicRect = pGraf->GetLogicRect();
2110 ImpSdrPdfImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect, aGraphic);
2111 if (aGraphic.getPageNumber() < aFilter.GetPageCount())
2113 nInsCnt = aFilter.DoImport(*pOL, nInsPos, aGraphic.getPageNumber(), pProgrInfo);
2115 #endif // HAVE_FEATURE_PDFIUM
2117 else if (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedVectorGraphicData() )
2119 GDIMetaFile aMetaFile(GetMetaFile(pGraf));
2120 if (aMetaFile.GetActionSize())
2122 aLogicRect = pGraf->GetLogicRect();
2123 ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect);
2124 nInsCnt = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo);
2129 SdrOle2Obj* pOle2 = dynamic_cast<SdrOle2Obj*>(pObj);
2130 if (pOle2 != nullptr && pOle2->GetGraphic())
2132 aLogicRect = pOle2->GetLogicRect();
2133 ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect);
2134 nInsCnt = aFilter.DoImport(pOle2->GetGraphic()->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo);
2137 if (nInsCnt != 0)
2139 // transformation
2140 GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat());
2141 size_t nObj = nInsPos;
2143 if (aGeoStat.nShearAngle)
2144 aGeoStat.RecalcTan();
2146 if (aGeoStat.nRotationAngle)
2147 aGeoStat.RecalcSinCos();
2149 for (size_t i = 0; i < nInsCnt; i++)
2151 if (bUndo)
2152 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));
2154 // update new MarkList
2155 SdrObject* pCandidate = pOL->GetObj(nObj);
2157 // apply original transformation
2158 if (aGeoStat.nShearAngle)
2159 pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.nShearAngle, aGeoStat.nTan, false);
2161 if (aGeoStat.nRotationAngle)
2162 pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.nRotationAngle, aGeoStat.nSin, aGeoStat.nCos);
2164 SdrMark aNewMark(pCandidate, pPV);
2165 aNewMarked.InsertEntry(aNewMark);
2167 nObj++;
2170 aForTheDescription.InsertEntry(*pM);
2172 if (bUndo)
2173 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
2175 // remove object from selection and delete
2176 GetMarkedObjectListWriteAccess().DeleteMark(TryToFindMarkedObject(pObj));
2177 pOL->RemoveObject(nInsPos-1);
2179 if (!bUndo)
2180 SdrObject::Free(pObj);
2184 if (aNewMarked.GetMarkCount())
2186 // create new selection
2187 for (size_t a = 0; a < aNewMarked.GetMarkCount(); ++a)
2189 GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
2192 SortMarkedObjects();
2195 if (bUndo)
2197 SetUndoComment(SvxResId(STR_EditImportMtf),aForTheDescription.GetMarkDescription());
2198 EndUndo();
2202 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */