sc: factor out common code
[LibreOffice.git] / svx / source / svdraw / svdedtv2.cxx
blob1dbc7afd83f18345a36beb067a72cec39ac8ac4b
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/strings.hrc>
44 #include <svx/svdoashp.hxx>
45 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
46 #include <i18nutil/unicode.hxx>
47 #include <sal/log.hxx>
48 #include <tools/debug.hxx>
49 #include <memory>
50 #include <vector>
51 #include <vcl/graph.hxx>
52 #include <svx/svxids.hrc>
53 #include <dstribut_enum.hxx>
54 #include <osl/diagnose.h>
56 using namespace com::sun::star;
58 SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const
60 return nullptr;
63 SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const
65 return nullptr;
68 void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, size_t /*nOldPos*/, size_t /*nNewPos*/)
72 void SdrEditView::MovMarkedToTop()
74 const SdrMarkList& rMarkList = GetMarkedObjectList();
75 const size_t nCount=rMarkList.GetMarkCount();
76 if (nCount==0)
77 return;
79 const bool bUndo = IsUndoEnabled();
81 if( bUndo )
82 BegUndo(SvxResId(STR_EditMovToTop),rMarkList.GetMarkDescription(),SdrRepeatFunc::MoveToTop);
84 rMarkList.ForceSort();
85 for (size_t nm=0; nm<nCount; ++nm)
86 { // All Ordnums have to be correct!
87 rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
89 bool bChg=false;
90 SdrObjList* pOL0=nullptr;
91 size_t nNewPos=0;
92 for (size_t nm=nCount; nm>0;)
94 --nm;
95 SdrMark* pM=rMarkList.GetMark(nm);
96 SdrObject* pObj=pM->GetMarkedSdrObj();
97 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
98 if (pOL!=pOL0)
100 nNewPos = pOL->GetObjCount()-1;
101 pOL0=pOL;
103 const size_t nNowPos = pObj->GetOrdNumDirect();
104 const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
105 size_t nCmpPos = nNowPos+1;
106 SdrObject* pMaxObj=GetMaxToTopObj(pObj);
107 if (pMaxObj!=nullptr)
109 size_t nMaxPos=pMaxObj->GetOrdNum();
110 if (nMaxPos!=0)
111 nMaxPos--;
112 if (nNewPos>nMaxPos)
113 nNewPos=nMaxPos; // neither go faster...
114 if (nNewPos<nNowPos)
115 nNewPos=nNowPos; // nor go in the other direction
117 bool bEnd=false;
118 while (nCmpPos<nNewPos && !bEnd)
120 assert(pOL);
121 SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
122 if (pCmpObj==nullptr)
124 OSL_FAIL("MovMarkedToTop(): Reference object not found.");
125 bEnd=true;
127 else if (pCmpObj==pMaxObj)
129 nNewPos=nCmpPos;
130 nNewPos--;
131 bEnd=true;
133 else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
135 nNewPos=nCmpPos;
136 bEnd=true;
138 else
140 nCmpPos++;
143 if (nNowPos!=nNewPos)
145 bChg=true;
146 pOL->SetObjectOrdNum(nNowPos,nNewPos);
147 if( bUndo )
148 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
149 ObjOrderChanged(pObj,nNowPos,nNewPos);
151 nNewPos--;
154 if( bUndo )
155 EndUndo();
157 if (bChg)
158 MarkListHasChanged();
161 void SdrEditView::MovMarkedToBtm()
163 const SdrMarkList& rMarkList = GetMarkedObjectList();
164 const size_t nCount=rMarkList.GetMarkCount();
165 if (nCount==0)
166 return;
168 const bool bUndo = IsUndoEnabled();
170 if( bUndo )
171 BegUndo(SvxResId(STR_EditMovToBtm),rMarkList.GetMarkDescription(),SdrRepeatFunc::MoveToBottom);
173 rMarkList.ForceSort();
174 for (size_t nm=0; nm<nCount; ++nm)
175 { // All Ordnums have to be correct!
176 rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
179 bool bChg=false;
180 SdrObjList* pOL0=nullptr;
181 size_t nNewPos=0;
182 for (size_t nm=0; nm<nCount; ++nm)
184 SdrMark* pM=rMarkList.GetMark(nm);
185 SdrObject* pObj=pM->GetMarkedSdrObj();
186 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
187 if (pOL!=pOL0)
189 nNewPos=0;
190 pOL0=pOL;
192 const size_t nNowPos = pObj->GetOrdNumDirect();
193 const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
194 size_t nCmpPos = nNowPos;
195 if (nCmpPos>0)
196 --nCmpPos;
197 SdrObject* pMaxObj=GetMaxToBtmObj(pObj);
198 if (pMaxObj!=nullptr)
200 const size_t nMinPos=pMaxObj->GetOrdNum()+1;
201 if (nNewPos<nMinPos)
202 nNewPos=nMinPos; // neither go faster...
203 if (nNewPos>nNowPos)
204 nNewPos=nNowPos; // nor go in the other direction
206 bool bEnd=false;
207 // nNewPos in this case is the "maximum" position
208 // the object may reach without going faster than the object before
209 // it (multiple selection).
210 while (nCmpPos>nNewPos && !bEnd)
212 assert(pOL);
213 SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
214 if (pCmpObj==nullptr)
216 OSL_FAIL("MovMarkedToBtm(): Reference object not found.");
217 bEnd=true;
219 else if (pCmpObj==pMaxObj)
221 nNewPos=nCmpPos;
222 nNewPos++;
223 bEnd=true;
225 else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
227 nNewPos=nCmpPos;
228 bEnd=true;
230 else
232 nCmpPos--;
235 if (nNowPos!=nNewPos)
237 bChg=true;
238 pOL->SetObjectOrdNum(nNowPos,nNewPos);
239 if( bUndo )
240 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
241 ObjOrderChanged(pObj,nNowPos,nNewPos);
243 nNewPos++;
246 if(bUndo)
247 EndUndo();
249 if(bChg)
250 MarkListHasChanged();
253 void SdrEditView::PutMarkedToTop()
255 PutMarkedInFrontOfObj(nullptr);
258 void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj)
260 const SdrMarkList& rMarkList = GetMarkedObjectList();
261 const size_t nCount=rMarkList.GetMarkCount();
262 if (nCount==0)
263 return;
265 const bool bUndo = IsUndoEnabled();
266 if( bUndo )
267 BegUndo(SvxResId(STR_EditPutToTop),rMarkList.GetMarkDescription(),SdrRepeatFunc::PutToTop);
269 rMarkList.ForceSort();
271 if (pRefObj!=nullptr)
273 // Make "in front of the object" work, even if the
274 // selected objects are already in front of the other object
275 const size_t nRefMark=rMarkList.FindObject(pRefObj);
276 SdrMark aRefMark;
277 if (nRefMark!=SAL_MAX_SIZE)
279 aRefMark=*rMarkList.GetMark(nRefMark);
280 GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
282 PutMarkedToBtm();
283 if (nRefMark!=SAL_MAX_SIZE)
285 GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
286 rMarkList.ForceSort();
289 for (size_t nm=0; nm<nCount; ++nm)
290 { // All Ordnums have to be correct!
291 rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
293 bool bChg=false;
294 SdrObjList* pOL0=nullptr;
295 size_t nNewPos=0;
296 for (size_t nm=nCount; nm>0;)
298 --nm;
299 SdrMark* pM=rMarkList.GetMark(nm);
300 SdrObject* pObj=pM->GetMarkedSdrObj();
301 if (pObj!=pRefObj)
303 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
304 if (pOL!=pOL0)
306 nNewPos=pOL->GetObjCount()-1;
307 pOL0=pOL;
309 const size_t nNowPos=pObj->GetOrdNumDirect();
310 SdrObject* pMaxObj=GetMaxToTopObj(pObj);
311 if (pMaxObj!=nullptr)
313 size_t nMaxOrd=pMaxObj->GetOrdNum(); // sadly doesn't work any other way
314 if (nMaxOrd>0)
315 nMaxOrd--;
316 if (nNewPos>nMaxOrd)
317 nNewPos=nMaxOrd; // neither go faster...
318 if (nNewPos<nNowPos)
319 nNewPos=nNowPos; // nor go into the other direction
321 if (pRefObj!=nullptr)
323 if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject())
325 const size_t nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way
326 if (nNewPos>nMaxOrd)
327 nNewPos=nMaxOrd; // neither go faster...
328 if (nNewPos<nNowPos)
329 nNewPos=nNowPos; // nor go into the other direction
331 else
333 nNewPos=nNowPos; // different PageView, so don't change
336 if (nNowPos!=nNewPos)
338 bChg=true;
339 pOL->SetObjectOrdNum(nNowPos,nNewPos);
340 if( bUndo )
341 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
342 ObjOrderChanged(pObj,nNowPos,nNewPos);
344 nNewPos--;
345 } // if (pObj!=pRefObj)
346 } // for loop over all selected objects
348 if( bUndo )
349 EndUndo();
351 if(bChg)
352 MarkListHasChanged();
355 void SdrEditView::PutMarkedToBtm()
357 PutMarkedBehindObj(nullptr);
360 void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj)
362 const SdrMarkList& rMarkList = GetMarkedObjectList();
363 const size_t nCount=rMarkList.GetMarkCount();
364 if (nCount==0)
365 return;
367 const bool bUndo = IsUndoEnabled();
369 if( bUndo )
370 BegUndo(SvxResId(STR_EditPutToBtm),rMarkList.GetMarkDescription(),SdrRepeatFunc::PutToBottom);
372 rMarkList.ForceSort();
373 if (pRefObj!=nullptr)
375 // Make "behind the object" work, even if the
376 // selected objects are already behind the other object
377 const size_t nRefMark=rMarkList.FindObject(pRefObj);
378 SdrMark aRefMark;
379 if (nRefMark!=SAL_MAX_SIZE)
381 aRefMark=*rMarkList.GetMark(nRefMark);
382 GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
384 PutMarkedToTop();
385 if (nRefMark!=SAL_MAX_SIZE)
387 GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
388 rMarkList.ForceSort();
391 for (size_t nm=0; nm<nCount; ++nm) { // All Ordnums have to be correct!
392 rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
394 bool bChg=false;
395 SdrObjList* pOL0=nullptr;
396 size_t nNewPos=0;
397 for (size_t nm=0; nm<nCount; ++nm) {
398 SdrMark* pM=rMarkList.GetMark(nm);
399 SdrObject* pObj=pM->GetMarkedSdrObj();
400 if (pObj!=pRefObj) {
401 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
402 if (pOL!=pOL0) {
403 nNewPos=0;
404 pOL0=pOL;
406 const size_t nNowPos=pObj->GetOrdNumDirect();
407 SdrObject* pMinObj=GetMaxToBtmObj(pObj);
408 if (pMinObj!=nullptr) {
409 const size_t nMinOrd=pMinObj->GetOrdNum()+1; // sadly doesn't work any differently
410 if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
411 if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
413 if (pRefObj!=nullptr) {
414 if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject()) {
415 const size_t nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently
416 if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
417 if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
418 } else {
419 nNewPos=nNowPos; // different PageView, so don't change
422 if (nNowPos!=nNewPos) {
423 bChg=true;
424 pOL->SetObjectOrdNum(nNowPos,nNewPos);
425 if( bUndo )
426 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
427 ObjOrderChanged(pObj,nNowPos,nNewPos);
429 nNewPos++;
430 } // if (pObj!=pRefObj)
431 } // for loop over all selected objects
433 if(bUndo)
434 EndUndo();
436 if(bChg)
437 MarkListHasChanged();
441 void SdrEditView::ReverseOrderOfMarked()
443 const SdrMarkList& rMarkList = GetMarkedObjectList();
444 rMarkList.ForceSort();
445 const size_t nMarkCount=rMarkList.GetMarkCount();
446 if (nMarkCount<=0)
447 return;
449 bool bChg=false;
451 bool bUndo = IsUndoEnabled();
452 if( bUndo )
453 BegUndo(SvxResId(STR_EditRevOrder),rMarkList.GetMarkDescription(),SdrRepeatFunc::ReverseOrder);
455 size_t a=0;
456 do {
457 // take into account selection across multiple PageViews
458 size_t b=a+1;
459 while (b<nMarkCount && rMarkList.GetMark(b)->GetPageView() == rMarkList.GetMark(a)->GetPageView()) ++b;
460 --b;
461 SdrObjList* pOL=rMarkList.GetMark(a)->GetPageView()->GetObjList();
462 size_t c=b;
463 if (a<c) { // make sure OrdNums aren't dirty
464 rMarkList.GetMark(a)->GetMarkedSdrObj()->GetOrdNum();
466 while (a<c) {
467 SdrObject* pObj1=rMarkList.GetMark(a)->GetMarkedSdrObj();
468 SdrObject* pObj2=rMarkList.GetMark(c)->GetMarkedSdrObj();
469 const size_t nOrd1=pObj1->GetOrdNumDirect();
470 const size_t nOrd2=pObj2->GetOrdNumDirect();
471 if( bUndo )
473 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2));
474 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1));
476 pOL->SetObjectOrdNum(nOrd1,nOrd2);
477 // Obj 2 has moved forward by one position, so now nOrd2-1
478 pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
479 // use Replace instead of SetOrdNum for performance reasons (recalculation of Ordnums)
480 ++a;
481 --c;
482 bChg=true;
484 a=b+1;
485 } while (a<nMarkCount);
487 if(bUndo)
488 EndUndo();
490 if(bChg)
491 MarkListHasChanged();
494 void SdrEditView::ImpCheckToTopBtmPossible()
496 const SdrMarkList& rMarkList = GetMarkedObjectList();
497 const size_t nCount=rMarkList.GetMarkCount();
498 if (nCount==0)
499 return;
500 if (nCount==1)
501 { // special-casing for single selection
502 SdrObject* pObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
503 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
504 SAL_WARN_IF(!pOL, "svx", "Object somehow has no ObjList");
505 size_t nMax = pOL ? pOL->GetObjCount() : 0;
506 size_t nMin = 0;
507 const size_t nObjNum=pObj->GetOrdNum();
508 SdrObject* pRestrict=GetMaxToTopObj(pObj);
509 if (pRestrict!=nullptr) {
510 const size_t nRestrict=pRestrict->GetOrdNum();
511 if (nRestrict<nMax) nMax=nRestrict;
513 pRestrict=GetMaxToBtmObj(pObj);
514 if (pRestrict!=nullptr) {
515 const size_t nRestrict=pRestrict->GetOrdNum();
516 if (nRestrict>nMin) nMin=nRestrict;
518 m_bToTopPossible = nObjNum+1 < nMax;
519 m_bToBtmPossible = nObjNum > nMin;
520 } else { // multiple selection
521 SdrObjList* pOL0=nullptr;
522 size_t nPos0 = 0;
523 for (size_t nm = 0; !m_bToBtmPossible && nm<nCount; ++nm) { // check 'send to background'
524 SdrObject* pObj=rMarkList.GetMark(nm)->GetMarkedSdrObj();
525 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
526 if (pOL!=pOL0) {
527 nPos0 = 0;
528 pOL0=pOL;
530 const size_t nPos = pObj->GetOrdNum();
531 m_bToBtmPossible = nPos && (nPos-1 > nPos0);
532 nPos0 = nPos;
535 pOL0=nullptr;
536 nPos0 = SAL_MAX_SIZE;
537 for (size_t nm=nCount; !m_bToTopPossible && nm>0; ) { // check 'bring to front'
538 --nm;
539 SdrObject* pObj=rMarkList.GetMark(nm)->GetMarkedSdrObj();
540 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
541 if (pOL!=pOL0) {
542 nPos0=pOL->GetObjCount();
543 pOL0=pOL;
545 const size_t nPos = pObj->GetOrdNum();
546 m_bToTopPossible = nPos+1 < nPos0;
547 nPos0=nPos;
553 // Combine
556 void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
558 if (pSource!=nullptr) {
559 SdrObjList* pOL=pSource->GetSubList();
560 if (pOL!=nullptr && !pSource->Is3DObj()) { // get first non-group object from group
561 SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
562 pSource=aIter.Next();
566 if(!(pSource && pDest))
567 return;
569 SfxItemSetFixed<SDRATTR_START, SDRATTR_NOTPERSIST_FIRST-1,
570 SDRATTR_NOTPERSIST_LAST+1, SDRATTR_END,
571 EE_ITEMS_START, EE_ITEMS_END> aSet(GetModel().GetItemPool());
573 aSet.Put(pSource->GetMergedItemSet());
575 pDest->ClearMergedItem();
576 pDest->SetMergedItemSet(aSet);
578 pDest->NbcSetLayer(pSource->GetLayer());
579 pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), true);
582 bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj)
584 // new condition IsLine() to be able to combine simple Lines
585 bool bIsLine(false);
587 const SdrPathObj* pPath = dynamic_cast< const SdrPathObj*>( pObj );
589 if(pPath)
591 bIsLine = pPath->IsLine();
594 SdrObjTransformInfoRec aInfo;
595 pObj->TakeObjInfo(aInfo);
597 return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine);
600 bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj)
602 SdrObjList* pOL = pObj->GetSubList();
604 if(pOL && !pObj->Is3DObj())
606 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
608 while(aIter.IsMore())
610 SdrObject* pObj1 = aIter.Next();
612 // all members of a group have to be convertible
613 if(!ImpCanConvertForCombine1(pObj1))
615 return false;
619 else
621 if(!ImpCanConvertForCombine1(pObj))
623 return false;
627 return true;
630 basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj)
632 basegfx::B2DPolyPolygon aRetval;
633 const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj );
635 if(pPath && !pObj->GetOutlinerParaObject())
637 aRetval = pPath->GetPathPoly();
639 else
641 rtl::Reference<SdrObject> pConvObj = pObj->ConvertToPolyObj(true/*bCombine*/, false);
643 if(pConvObj)
645 SdrObjList* pOL = pConvObj->GetSubList();
647 if(pOL)
649 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
651 while(aIter.IsMore())
653 SdrObject* pObj1 = aIter.Next();
654 pPath = dynamic_cast<SdrPathObj*>( pObj1 );
656 if(pPath)
658 aRetval.append(pPath->GetPathPoly());
662 else
664 pPath = dynamic_cast<SdrPathObj*>( pConvObj.get() );
666 if(pPath)
668 aRetval = pPath->GetPathPoly();
674 return aRetval;
677 basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj)
679 SdrObjList* pOL = pObj->GetSubList();
681 if(pOL && !pObj->Is3DObj())
683 basegfx::B2DPolyPolygon aRetval;
684 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
686 while(aIter.IsMore())
688 SdrObject* pObj1 = aIter.Next();
689 aRetval.append(ImpGetPolyPolygon1(pObj1));
692 return aRetval;
694 else
696 return ImpGetPolyPolygon1(pObj);
700 basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
702 const sal_uInt32 nPolyCount(rPolyPolygon.count());
704 if(0 == nPolyCount)
706 return basegfx::B2DPolygon();
708 else if(1 == nPolyCount)
710 return rPolyPolygon.getB2DPolygon(0);
712 else
714 basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0));
716 for(sal_uInt32 a(1); a < nPolyCount; a++)
718 basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
720 if(aRetval.count())
722 if(aCandidate.count())
724 const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0));
725 const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1));
726 const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0));
727 const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1));
729 const double fRACA(basegfx::B2DVector(aCA - aRA).getLength());
730 const double fRACB(basegfx::B2DVector(aCB - aRA).getLength());
731 const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength());
732 const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength());
734 const double fSmallestRA(std::min(fRACA, fRACB));
735 const double fSmallestRB(std::min(fRBCA, fRBCB));
737 if(fSmallestRA < fSmallestRB)
739 // flip result
740 aRetval.flip();
743 const double fSmallestCA(std::min(fRACA, fRBCA));
744 const double fSmallestCB(std::min(fRACB, fRBCB));
746 if(fSmallestCB < fSmallestCA)
748 // flip candidate
749 aCandidate.flip();
752 // append candidate to retval
753 aRetval.append(aCandidate);
756 else
758 aRetval = std::move(aCandidate);
762 return aRetval;
766 namespace {
768 // for distribution dialog function
769 struct ImpDistributeEntry
771 SdrObject* mpObj;
772 sal_Int32 mnPos;
773 sal_Int32 mnLength;
778 typedef std::vector<ImpDistributeEntry> ImpDistributeEntryList;
780 void SdrEditView::DistributeMarkedObjects(sal_uInt16 SlotID)
782 const SdrMarkList& rMarkList = GetMarkedObjectList();
783 const size_t nMark(rMarkList.GetMarkCount());
785 if(nMark <= 2)
786 return;
788 SvxDistributeHorizontal eHor = SvxDistributeHorizontal::NONE;
789 SvxDistributeVertical eVer = SvxDistributeVertical::NONE;
791 switch (SlotID)
793 case SID_DISTRIBUTE_HLEFT: eHor = SvxDistributeHorizontal::Left; break;
794 case SID_DISTRIBUTE_HCENTER: eHor = SvxDistributeHorizontal::Center; break;
795 case SID_DISTRIBUTE_HDISTANCE: eHor = SvxDistributeHorizontal::Distance; break;
796 case SID_DISTRIBUTE_HRIGHT: eHor = SvxDistributeHorizontal::Right; break;
797 case SID_DISTRIBUTE_VTOP: eVer = SvxDistributeVertical::Top; break;
798 case SID_DISTRIBUTE_VCENTER: eVer = SvxDistributeVertical::Center; break;
799 case SID_DISTRIBUTE_VDISTANCE: eVer = SvxDistributeVertical::Distance; break;
800 case SID_DISTRIBUTE_VBOTTOM: eVer = SvxDistributeVertical::Bottom; break;
803 ImpDistributeEntryList aEntryList;
804 ImpDistributeEntryList::iterator itEntryList;
805 sal_uInt32 nFullLength;
807 const bool bUndo = IsUndoEnabled();
808 if( bUndo )
809 BegUndo();
811 if(eHor != SvxDistributeHorizontal::NONE)
813 // build sorted entry list
814 nFullLength = 0;
816 for( size_t a = 0; a < nMark; ++a )
818 SdrMark* pMark = rMarkList.GetMark(a);
819 ImpDistributeEntry aNew;
821 aNew.mpObj = pMark->GetMarkedSdrObj();
823 switch(eHor)
825 case SvxDistributeHorizontal::Left:
827 aNew.mnPos = aNew.mpObj->GetSnapRect().Left();
828 break;
830 case SvxDistributeHorizontal::Center:
832 aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
833 break;
835 case SvxDistributeHorizontal::Distance:
837 aNew.mnLength = aNew.mpObj->GetSnapRect().GetWidth() + 1;
838 nFullLength += aNew.mnLength;
839 aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
840 break;
842 case SvxDistributeHorizontal::Right:
844 aNew.mnPos = aNew.mpObj->GetSnapRect().Right();
845 break;
847 default: break;
850 itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
851 [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
852 if ( itEntryList < aEntryList.end() )
853 aEntryList.insert( itEntryList, aNew );
854 else
855 aEntryList.push_back( aNew );
858 if(eHor == SvxDistributeHorizontal::Distance)
860 // calculate room in-between
861 sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1;
862 double fStepWidth = (static_cast<double>(nWidth) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
863 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
864 fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
866 // move entries 1..n-1
867 for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i )
869 ImpDistributeEntry& rCurr = aEntryList[ i ];
870 ImpDistributeEntry& rNext = aEntryList[ i + 1];
871 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
872 if( bUndo )
873 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
874 rCurr.mpObj->Move(Size(nDelta, 0));
875 fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
878 else
880 // calculate distances
881 sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
882 double fStepWidth = static_cast<double>(nWidth) / static_cast<double>(aEntryList.size() - 1);
883 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
884 fStepStart += fStepWidth;
886 // move entries 1..n-1
887 for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i )
889 ImpDistributeEntry& rCurr = aEntryList[ i ];
890 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
891 if( bUndo )
892 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
893 rCurr.mpObj->Move(Size(nDelta, 0));
894 fStepStart += fStepWidth;
898 // clear list
899 aEntryList.clear();
902 if(eVer != SvxDistributeVertical::NONE)
904 // build sorted entry list
905 nFullLength = 0;
907 for( size_t a = 0; a < nMark; ++a )
909 SdrMark* pMark = rMarkList.GetMark(a);
910 ImpDistributeEntry aNew;
912 aNew.mpObj = pMark->GetMarkedSdrObj();
914 switch(eVer)
916 case SvxDistributeVertical::Top:
918 aNew.mnPos = aNew.mpObj->GetSnapRect().Top();
919 break;
921 case SvxDistributeVertical::Center:
923 aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
924 break;
926 case SvxDistributeVertical::Distance:
928 aNew.mnLength = aNew.mpObj->GetSnapRect().GetHeight() + 1;
929 nFullLength += aNew.mnLength;
930 aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
931 break;
933 case SvxDistributeVertical::Bottom:
935 aNew.mnPos = aNew.mpObj->GetSnapRect().Bottom();
936 break;
938 default: break;
941 itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
942 [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
943 if ( itEntryList < aEntryList.end() )
944 aEntryList.insert( itEntryList, aNew );
945 else
946 aEntryList.push_back( aNew );
949 if(eVer == SvxDistributeVertical::Distance)
951 // calculate room in-between
952 sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1;
953 double fStepWidth = (static_cast<double>(nHeight) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
954 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
955 fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
957 // move entries 1..n-1
958 for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
960 ImpDistributeEntry& rCurr = aEntryList[ i ];
961 ImpDistributeEntry& rNext = aEntryList[ i + 1 ];
962 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
963 if( bUndo )
964 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
965 rCurr.mpObj->Move(Size(0, nDelta));
966 fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
969 else
971 // calculate distances
972 sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
973 double fStepWidth = static_cast<double>(nHeight) / static_cast<double>(aEntryList.size() - 1);
974 double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
975 fStepStart += fStepWidth;
977 // move entries 1..n-1
978 for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
980 ImpDistributeEntry& rCurr = aEntryList[ i ];
981 sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
982 if( bUndo )
983 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
984 rCurr.mpObj->Move(Size(0, nDelta));
985 fStepStart += fStepWidth;
989 // clear list
990 aEntryList.clear();
993 // UNDO-Comment and end of UNDO
994 GetModel().SetUndoComment(SvxResId(STR_DistributeMarkedObjects));
996 if( bUndo )
997 EndUndo();
1000 void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode)
1002 const SdrMarkList& rMarkList = GetMarkedObjectList();
1003 // #i73441# check content
1004 if(rMarkList.GetMarkCount() == 0)
1005 return;
1007 SdrMarkList aRemove;
1008 rMarkList.ForceSort();
1010 const bool bUndo = IsUndoEnabled();
1012 if( bUndo )
1013 BegUndo();
1015 size_t nInsPos = SAL_MAX_SIZE;
1016 const SdrObject* pAttrObj = nullptr;
1017 basegfx::B2DPolyPolygon aMergePolyPolygonA;
1018 basegfx::B2DPolyPolygon aMergePolyPolygonB;
1020 SdrObjList* pInsOL = nullptr;
1021 SdrPageView* pInsPV = nullptr;
1022 bool bFirstObjectComplete(false);
1024 // make sure selected objects are contour objects
1025 // since now basegfx::utils::adaptiveSubdivide() is used, it is no longer
1026 // necessary to use ConvertMarkedToPolyObj which will subdivide curves using the old
1027 // mechanisms. In a next step the polygon clipper will even be able to clip curves...
1028 // ConvertMarkedToPolyObj(true);
1029 ConvertMarkedToPathObj(true);
1030 OSL_ENSURE(rMarkList.GetMarkCount() != 0, "no more objects selected after preparations (!)");
1032 for(size_t a=0; a<rMarkList.GetMarkCount(); ++a)
1034 SdrMark* pM = rMarkList.GetMark(a);
1035 SdrObject* pObj = pM->GetMarkedSdrObj();
1037 if(ImpCanConvertForCombine(pObj))
1039 if(!pAttrObj)
1040 pAttrObj = pObj;
1042 nInsPos = pObj->GetOrdNum() + 1;
1043 pInsPV = pM->GetPageView();
1044 pInsOL = pObj->getParentSdrObjListFromSdrObject();
1046 // #i76891# use single iteration from SJ here which works on SdrObjects and takes
1047 // groups into account by itself
1048 SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
1050 while(aIter.IsMore())
1052 SdrObject* pCandidate = aIter.Next();
1053 SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>( pCandidate );
1054 if(pPathObj)
1056 basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly());
1058 // #i76891# unfortunately ConvertMarkedToPathObj has converted all
1059 // involved polygon data to curve segments, even if not necessary.
1060 // It is better to try to reduce to more simple polygons.
1061 aTmpPoly = basegfx::utils::simplifyCurveSegments(aTmpPoly);
1063 // for each part polygon as preparation, remove self-intersections
1064 // correct orientations and get rid of possible neutral polygons.
1065 aTmpPoly = basegfx::utils::prepareForPolygonOperation(aTmpPoly);
1067 if(!bFirstObjectComplete)
1069 // #i111987# Also need to collect ORed source shape when more than
1070 // a single polygon is involved
1071 if(aMergePolyPolygonA.count())
1073 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly);
1075 else
1077 aMergePolyPolygonA = std::move(aTmpPoly);
1080 else
1082 if(aMergePolyPolygonB.count())
1084 // to topologically correctly collect the 2nd polygon
1085 // group it is necessary to OR the parts (each is seen as
1086 // XOR-FillRule polygon and they are drawn over each-other)
1087 aMergePolyPolygonB = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly);
1089 else
1091 aMergePolyPolygonB = std::move(aTmpPoly);
1097 // was there something added to the first polygon?
1098 if(!bFirstObjectComplete && aMergePolyPolygonA.count())
1100 bFirstObjectComplete = true;
1103 // move object to temporary delete list
1104 aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView()));
1108 switch(eMode)
1110 case SdrMergeMode::Merge:
1112 // merge all contained parts (OR)
1113 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
1114 break;
1116 case SdrMergeMode::Subtract:
1118 // Subtract B from A
1119 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB);
1120 break;
1122 case SdrMergeMode::Intersect:
1124 // AND B and A
1125 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
1126 break;
1130 // #i73441# check insert list before taking actions
1131 if(pInsOL)
1133 rtl::Reference<SdrPathObj> pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), SdrObjKind::PathFill, std::move(aMergePolyPolygonA));
1134 ImpCopyAttributes(pAttrObj, pPath.get());
1135 pInsOL->InsertObject(pPath.get(), nInsPos);
1136 if( bUndo )
1137 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath));
1139 // #i124760# To have a correct selection with only the new object it is necessary to
1140 // unmark all objects first. If not doing so, there may remain invalid pointers to objects
1141 // TTTT:Not needed for aw080 (!)
1142 UnmarkAllObj(pInsPV);
1144 MarkObj(pPath.get(), pInsPV, false, true);
1147 aRemove.ForceSort();
1148 switch(eMode)
1150 case SdrMergeMode::Merge:
1152 SetUndoComment(
1153 SvxResId(STR_EditMergeMergePoly),
1154 aRemove.GetMarkDescription());
1155 break;
1157 case SdrMergeMode::Subtract:
1159 SetUndoComment(
1160 SvxResId(STR_EditMergeSubstractPoly),
1161 aRemove.GetMarkDescription());
1162 break;
1164 case SdrMergeMode::Intersect:
1166 SetUndoComment(
1167 SvxResId(STR_EditMergeIntersectPoly),
1168 aRemove.GetMarkDescription());
1169 break;
1172 DeleteMarkedList(aRemove);
1174 if( bUndo )
1175 EndUndo();
1178 void SdrEditView::EqualizeMarkedObjects(bool bWidth)
1180 const SdrMarkList& rMarkList = GetMarkedObjectList();
1181 size_t nMarked = rMarkList.GetMarkCount();
1183 if (nMarked < 2)
1184 return;
1186 size_t nLastSelected = 0;
1187 sal_Int64 nLastSelectedTime = rMarkList.GetMark(0)->getTimeStamp();
1188 for (size_t a = 1; a < nMarked; ++a)
1190 sal_Int64 nCandidateTime = rMarkList.GetMark(a)->getTimeStamp();
1191 if (nCandidateTime > nLastSelectedTime)
1193 nLastSelectedTime = nCandidateTime;
1194 nLastSelected = a;
1198 SdrObject* pLastSelectedObj = rMarkList.GetMark(nLastSelected)->GetMarkedSdrObj();
1199 Size aLastRectSize(pLastSelectedObj->GetLogicRect().GetSize());
1201 const bool bUndo = IsUndoEnabled();
1203 if (bUndo)
1204 BegUndo();
1206 for (size_t a = 0; a < nMarked; ++a)
1208 if (a == nLastSelected)
1209 continue;
1210 SdrMark* pM = rMarkList.GetMark(a);
1211 SdrObject* pObj = pM->GetMarkedSdrObj();
1212 tools::Rectangle aLogicRect(pObj->GetLogicRect());
1213 Size aLogicRectSize(aLogicRect.GetSize());
1214 if (bWidth)
1215 aLogicRectSize.setWidth( aLastRectSize.Width() );
1216 else
1217 aLogicRectSize.setHeight( aLastRectSize.Height() );
1218 aLogicRect.SetSize(aLogicRectSize);
1219 if (bUndo)
1220 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
1221 pObj->SetLogicRect(aLogicRect);
1224 SetUndoComment(
1225 SvxResId(bWidth ? STR_EqualizeWidthMarkedObjects : STR_EqualizeHeightMarkedObjects),
1226 rMarkList.GetMarkDescription());
1228 if (bUndo)
1229 EndUndo();
1232 void SdrEditView::CombineMarkedTextObjects()
1234 SdrPageView* pPageView = GetSdrPageView();
1235 if ( !pPageView || pPageView->IsLayerLocked( GetActiveLayer() ) )
1236 return;
1238 bool bUndo = IsUndoEnabled();
1240 // Undo-String will be set later
1241 if ( bUndo )
1242 BegUndo();
1244 SdrOutliner& rDrawOutliner = getSdrModelFromSdrView().GetDrawOutliner();
1246 const SdrMarkList& rMarkList = GetMarkedObjectList();
1247 SdrObjListIter aIter( rMarkList, SdrIterMode::Flat);
1248 while ( aIter.IsMore() )
1250 SdrObject* pObj = aIter.Next();
1251 SdrTextObj* pTextObj = DynCastSdrTextObj( pObj );
1252 const OutlinerParaObject* pOPO = pTextObj ? pTextObj->GetOutlinerParaObject() : nullptr;
1253 if ( pOPO && pTextObj->IsTextFrame()
1254 && pTextObj->GetObjIdentifier() == SdrObjKind::Text // not callouts (OBJ_CAPTION)
1255 && !pTextObj->IsOutlText() // not impress presentation objects
1256 && pTextObj->GetMergedItem(XATTR_FORMTXTSTYLE).GetValue() == XFormTextStyle::NONE // not Fontwork
1259 // if the last paragraph does not end in paragraph-end punctuation (ignoring whitespace),
1260 // assume this text should be added to the end of the last paragraph, instead of starting a new paragraph.
1261 const sal_Int32 nPara = rDrawOutliner.GetParagraphCount();
1262 const OUString sLastPara = nPara ? rDrawOutliner.GetText( rDrawOutliner.GetParagraph( nPara - 1 ) ) : u""_ustr;
1263 sal_Int32 n = sLastPara.getLength();
1264 while ( n && unicode::isWhiteSpace( sLastPara[--n] ) )
1266 //TODO: find way to use Locale to identify sentence final punctuation. Copied IsSentenceAtEnd() from autofmt.cxx
1267 const bool bAppend = !n || ( sLastPara[n] != '.' && sLastPara[n] != '?' && sLastPara[n] != '!' );
1268 rDrawOutliner.AddText( *pOPO, bAppend );
1270 else
1272 // Unmark non-textboxes, because all marked objects are deleted at the end. AdjustMarkHdl later.
1273 MarkObj(pObj, pPageView, /*bUnmark=*/true, /*bImpNoSetMarkHdl=*/true);
1277 MarkListHasChanged();
1278 AdjustMarkHdl();
1280 if ( rMarkList.GetMarkCount() > 1 )
1282 rtl::Reference<SdrRectObj> pReplacement = new SdrRectObj( getSdrModelFromSdrView(), SdrObjKind::Text );
1283 pReplacement->SetOutlinerParaObject( rDrawOutliner.CreateParaObject() );
1284 pReplacement->SetSnapRect( GetMarkedObjRect() );
1286 const SdrInsertFlags nFlags = SdrInsertFlags::DONTMARK | SdrInsertFlags::SETDEFLAYER;
1287 if ( InsertObjectAtView( pReplacement.get(), *pPageView, nFlags ) )
1288 DeleteMarkedObj();
1291 if ( bUndo )
1292 EndUndo();
1294 return;
1297 void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly)
1299 // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would
1300 // create a 2nd Undo-action and Undo-Comment.
1302 bool bUndo = IsUndoEnabled();
1304 // Undo-String will be set later
1305 if( bUndo )
1306 BegUndo(u""_ustr, u""_ustr, bNoPolyPoly ? SdrRepeatFunc::CombineOnePoly : SdrRepeatFunc::CombinePolyPoly);
1308 // #105899# First, guarantee that all objects are converted to polyobjects,
1309 // especially for SdrGrafObj with bitmap filling this is necessary to not
1310 // lose the bitmap filling.
1312 // #i12392#
1313 // ConvertMarkedToPolyObj was too strong here, it will lose quality and
1314 // information when curve objects are combined. This can be replaced by
1315 // using ConvertMarkedToPathObj without changing the previous fix.
1317 // #i21250#
1318 // Instead of simply passing true as LineToArea, use bNoPolyPoly as info
1319 // if this command is a 'Combine' or a 'Connect' command. On Connect it's true.
1320 // To not concert line segments with a set line width to polygons in that case,
1321 // use this info. Do not convert LineToArea on Connect commands.
1322 // ConvertMarkedToPathObj(!bNoPolyPoly);
1324 // This is used for Combine and Connect. In no case it is necessary to force
1325 // the content to curve, but it is also not good to force to polygons. Thus,
1326 // curve is the less information losing one. Remember: This place is not
1327 // used for merge.
1328 // LineToArea is never necessary, both commands are able to take over the
1329 // set line style and to display it correctly. Thus, i will use a
1330 // ConvertMarkedToPathObj with a false in any case. Only drawback is that
1331 // simple polygons will be changed to curves, but with no information loss.
1332 ConvertMarkedToPathObj(false /* bLineToArea */);
1334 // continue as before
1335 basegfx::B2DPolyPolygon aPolyPolygon;
1336 SdrObjList* pCurrentOL = nullptr;
1337 SdrMarkList aRemoveBuffer;
1339 const SdrMarkList& rMarkList = GetMarkedObjectList();
1340 rMarkList.ForceSort();
1341 size_t nInsPos = SAL_MAX_SIZE;
1342 SdrObjList* pInsOL = nullptr;
1343 SdrPageView* pInsPV = nullptr;
1344 const SdrObject* pAttrObj = nullptr;
1346 for(size_t a = rMarkList.GetMarkCount(); a; )
1348 --a;
1349 SdrMark* pM = rMarkList.GetMark(a);
1350 SdrObject* pObj = pM->GetMarkedSdrObj();
1351 SdrObjList* pThisOL = pObj->getParentSdrObjListFromSdrObject();
1353 if(pCurrentOL != pThisOL)
1355 pCurrentOL = pThisOL;
1358 if(ImpCanConvertForCombine(pObj))
1360 // remember objects to be able to copy attributes
1361 pAttrObj = pObj;
1363 // unfortunately ConvertMarkedToPathObj has converted all
1364 // involved polygon data to curve segments, even if not necessary.
1365 // It is better to try to reduce to more simple polygons.
1366 basegfx::B2DPolyPolygon aTmpPoly(basegfx::utils::simplifyCurveSegments(ImpGetPolyPolygon(pObj)));
1367 aPolyPolygon.insert(0, aTmpPoly);
1369 if(!pInsOL)
1371 nInsPos = pObj->GetOrdNum() + 1;
1372 pInsPV = pM->GetPageView();
1373 pInsOL = pObj->getParentSdrObjListFromSdrObject();
1376 aRemoveBuffer.InsertEntry(SdrMark(pObj, pM->GetPageView()));
1380 if(bNoPolyPoly)
1382 basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon));
1383 aPolyPolygon.clear();
1384 aPolyPolygon.append(aCombinedPolygon);
1387 const sal_uInt32 nPolyCount(aPolyPolygon.count());
1389 if (nPolyCount && pAttrObj)
1391 SdrObjKind eKind = SdrObjKind::PathFill;
1393 if(nPolyCount > 1)
1395 aPolyPolygon.setClosed(true);
1397 else
1399 // check for Polyline
1400 const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1401 const sal_uInt32 nPointCount(aPolygon.count());
1403 if(nPointCount <= 2)
1405 eKind = SdrObjKind::PathLine;
1407 else
1409 if(!aPolygon.isClosed())
1411 const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0));
1412 const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1));
1413 const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength());
1414 const double fJoinTolerance(10.0);
1416 if(fDistance < fJoinTolerance)
1418 aPolyPolygon.setClosed(true);
1420 else
1422 eKind = SdrObjKind::PathLine;
1428 rtl::Reference<SdrPathObj> pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), eKind, std::move(aPolyPolygon));
1430 // attributes of the lowest object
1431 ImpCopyAttributes(pAttrObj, pPath.get());
1433 // If LineStyle of pAttrObj is drawing::LineStyle_NONE force to drawing::LineStyle_SOLID to make visible.
1434 const drawing::LineStyle eLineStyle = pAttrObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
1435 const drawing::FillStyle eFillStyle = pAttrObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
1437 // Take fill style/closed state of pAttrObj in account when deciding to change the line style
1438 bool bIsClosedPathObj = false;
1439 if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pAttrObj))
1440 if (pPathObj->IsClosed())
1441 bIsClosedPathObj = true;
1443 if(drawing::LineStyle_NONE == eLineStyle && (drawing::FillStyle_NONE == eFillStyle || !bIsClosedPathObj))
1445 pPath->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID));
1448 pInsOL->InsertObject(pPath.get(),nInsPos);
1449 if( bUndo )
1450 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath));
1452 // Here was a severe error: Without UnmarkAllObj, the new object was marked
1453 // additionally to the two ones which are deleted below. As long as those are
1454 // in the UNDO there is no problem, but as soon as they get deleted, the
1455 // MarkList will contain deleted objects -> GPF.
1456 UnmarkAllObj(pInsPV);
1457 MarkObj(pPath.get(), pInsPV, false, true);
1460 // build an UndoComment from the objects actually used
1461 aRemoveBuffer.ForceSort(); // important for remove (see below)
1462 if( bUndo )
1463 SetUndoComment(SvxResId(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveBuffer.GetMarkDescription());
1465 // remove objects actually used from the list
1466 DeleteMarkedList(aRemoveBuffer);
1467 if( bUndo )
1468 EndUndo();
1472 // Dismantle
1475 bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, bool bMakeLines)
1477 bool bCan(false);
1478 const sal_uInt32 nPolygonCount(rPpolyPolygon.count());
1480 if(nPolygonCount >= 2)
1482 // #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon
1483 bCan = true;
1485 else if(bMakeLines && 1 == nPolygonCount)
1487 // #i69172# ..or with at least 2 edges (curves or lines)
1488 const basegfx::B2DPolygon& aPolygon(rPpolyPolygon.getB2DPolygon(0));
1489 const sal_uInt32 nPointCount(aPolygon.count());
1491 if(nPointCount > 2)
1493 bCan = true;
1497 return bCan;
1500 bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, bool bMakeLines)
1502 bool bOtherObjs(false); // true=objects other than PathObj's existent
1503 bool bMin1PolyPoly(false); // true=at least 1 tools::PolyPolygon with more than one Polygon existent
1504 SdrObjList* pOL = pObj->GetSubList();
1506 if(pOL)
1508 // group object -- check all members if they're PathObjs
1509 SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
1511 while(aIter.IsMore() && !bOtherObjs)
1513 const SdrObject* pObj1 = aIter.Next();
1514 const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj1 );
1516 if(pPath)
1518 if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines))
1520 bMin1PolyPoly = true;
1523 SdrObjTransformInfoRec aInfo;
1524 pObj1->TakeObjInfo(aInfo);
1526 if(!aInfo.bCanConvToPath)
1528 // happens e. g. in the case of FontWork
1529 bOtherObjs = true;
1532 else
1534 bOtherObjs = true;
1538 else
1540 const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>(pObj);
1541 const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>(pObj);
1543 // #i37011#
1544 if(pPath)
1546 if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines))
1548 bMin1PolyPoly = true;
1551 SdrObjTransformInfoRec aInfo;
1552 pObj->TakeObjInfo(aInfo);
1554 // new condition IsLine() to be able to break simple Lines
1555 if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine())
1557 // happens e. g. in the case of FontWork
1558 bOtherObjs = true;
1561 else if(pCustomShape)
1563 if(bMakeLines)
1565 // allow break command
1566 bMin1PolyPoly = true;
1569 else
1571 bOtherObjs = true;
1574 return bMin1PolyPoly && !bOtherObjs;
1577 void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, size_t& rPos, SdrPageView* pPV, bool bMakeLines)
1579 const SdrPathObj* pSrcPath = dynamic_cast<const SdrPathObj*>( pObj );
1580 const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>( pObj );
1582 const bool bUndo = IsUndoEnabled();
1584 if(pSrcPath)
1586 // #i74631# redesigned due to XpolyPolygon removal and explicit constructors
1587 SdrObject* pLast = nullptr; // to be able to apply OutlinerParaObject
1588 const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly());
1589 const sal_uInt32 nPolyCount(rPolyPolygon.count());
1591 for(sal_uInt32 a(0); a < nPolyCount; a++)
1593 const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a));
1594 const sal_uInt32 nPointCount(rCandidate.count());
1596 if(!bMakeLines || nPointCount < 2)
1598 rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
1599 pSrcPath->getSdrModelFromSdrObject(),
1600 pSrcPath->GetObjIdentifier(),
1601 basegfx::B2DPolyPolygon(rCandidate));
1602 ImpCopyAttributes(pSrcPath, pPath.get());
1603 pLast = pPath.get();
1604 rOL.InsertObject(pPath.get(), rPos);
1605 if( bUndo )
1606 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
1607 MarkObj(pPath.get(), pPV, false, true);
1608 rPos++;
1610 else
1612 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
1614 for(sal_uInt32 b(0); b < nLoopCount; b++)
1616 SdrObjKind eKind(SdrObjKind::PolyLine);
1617 basegfx::B2DPolygon aNewPolygon;
1618 const sal_uInt32 nNextIndex((b + 1) % nPointCount);
1620 aNewPolygon.append(rCandidate.getB2DPoint(b));
1622 if(rCandidate.areControlPointsUsed())
1624 aNewPolygon.appendBezierSegment(
1625 rCandidate.getNextControlPoint(b),
1626 rCandidate.getPrevControlPoint(nNextIndex),
1627 rCandidate.getB2DPoint(nNextIndex));
1628 eKind = SdrObjKind::PathLine;
1630 else
1632 aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex));
1635 rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
1636 pSrcPath->getSdrModelFromSdrObject(),
1637 eKind,
1638 basegfx::B2DPolyPolygon(aNewPolygon));
1639 ImpCopyAttributes(pSrcPath, pPath.get());
1640 pLast = pPath.get();
1641 rOL.InsertObject(pPath.get(), rPos);
1642 if( bUndo )
1643 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
1644 MarkObj(pPath.get(), pPV, false, true);
1645 rPos++;
1650 if(pLast && pSrcPath->GetOutlinerParaObject())
1652 pLast->SetOutlinerParaObject(*pSrcPath->GetOutlinerParaObject());
1655 else if(pCustomShape)
1657 if(bMakeLines)
1659 // break up custom shape
1660 const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape();
1662 if(pReplacement)
1664 rtl::Reference<SdrObject> pCandidate(pReplacement->CloneSdrObject(pReplacement->getSdrModelFromSdrObject()));
1665 DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)");
1667 if(pCustomShape->GetMergedItem(SDRATTR_SHADOW).GetValue())
1669 if(dynamic_cast<const SdrObjGroup*>( pReplacement) != nullptr)
1671 pCandidate->SetMergedItem(makeSdrShadowItem(true));
1675 rOL.InsertObject(pCandidate.get(), rPos);
1676 if( bUndo )
1677 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true));
1678 MarkObj(pCandidate.get(), pPV, false, true);
1680 if(pCustomShape->HasText() && !pCustomShape->IsTextPath())
1682 // #i37011# also create a text object and add at rPos + 1
1683 rtl::Reference<SdrObject> pTextObj = SdrObjFactory::MakeNewObject(
1684 pCustomShape->getSdrModelFromSdrObject(),
1685 pCustomShape->GetObjInventor(),
1686 SdrObjKind::Text);
1688 // Copy text content
1689 OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject();
1690 if(pParaObj)
1692 pTextObj->NbcSetOutlinerParaObject(*pParaObj);
1695 // copy all attributes
1696 SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet());
1698 // clear fill and line style
1699 aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
1700 aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
1702 // get the text bounds and set at text object
1703 tools::Rectangle aTextBounds = pCustomShape->GetSnapRect();
1704 if(pCustomShape->GetTextBounds(aTextBounds))
1706 pTextObj->SetSnapRect(aTextBounds);
1709 // if rotated, copy GeoStat, too.
1710 const GeoStat& rSourceGeo = pCustomShape->GetGeoStat();
1711 if(rSourceGeo.m_nRotationAngle)
1713 pTextObj->NbcRotate(
1714 pCustomShape->GetSnapRect().Center(), rSourceGeo.m_nRotationAngle,
1715 rSourceGeo.mfSinRotationAngle, rSourceGeo.mfCosRotationAngle);
1718 // set modified ItemSet at text object
1719 pTextObj->SetMergedItemSet(aTargetItemSet);
1721 // insert object
1722 rOL.InsertObject(pTextObj.get(), rPos + 1);
1723 if( bUndo )
1724 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true));
1725 MarkObj(pTextObj.get(), pPV, false, true);
1732 void SdrEditView::DismantleMarkedObjects(bool bMakeLines)
1734 // temporary MarkList
1735 SdrMarkList aRemoveBuffer;
1737 const SdrMarkList& rMarkList = GetMarkedObjectList();
1738 rMarkList.ForceSort();
1740 const bool bUndo = IsUndoEnabled();
1742 if( bUndo )
1744 // comment is constructed later
1745 BegUndo(u""_ustr, u""_ustr, bMakeLines ? SdrRepeatFunc::DismantleLines : SdrRepeatFunc::DismantlePolys);
1748 SdrObjList* pOL0=nullptr;
1749 const bool bWasLocked = GetModel().isLocked();
1750 GetModel().setLock(true);
1751 for (size_t nm=rMarkList.GetMarkCount(); nm>0;) {
1752 --nm;
1753 SdrMark* pM=rMarkList.GetMark(nm);
1754 SdrObject* pObj=pM->GetMarkedSdrObj();
1755 SdrPageView* pPV=pM->GetPageView();
1756 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
1757 if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // make sure OrdNums are correct!
1758 if (ImpCanDismantle(pObj,bMakeLines)) {
1759 assert(pOL);
1760 aRemoveBuffer.InsertEntry(SdrMark(pObj,pM->GetPageView()));
1761 const size_t nPos0=pObj->GetOrdNumDirect();
1762 size_t nPos=nPos0+1;
1763 SdrObjList* pSubList=pObj->GetSubList();
1764 if (pSubList!=nullptr && !pObj->Is3DObj()) {
1765 SdrObjListIter aIter(pSubList,SdrIterMode::DeepNoGroups);
1766 while (aIter.IsMore()) {
1767 const SdrObject* pObj1=aIter.Next();
1768 ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines);
1770 } else {
1771 ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines);
1773 if( bUndo )
1774 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,true));
1775 pOL->RemoveObject(nPos0);
1778 GetModel().setLock(bWasLocked);
1780 if( bUndo )
1782 // construct UndoComment from objects actually used
1783 SetUndoComment(SvxResId(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveBuffer.GetMarkDescription());
1784 // remove objects actually used from the list
1785 EndUndo();
1790 // Group
1793 void SdrEditView::GroupMarked()
1795 const SdrMarkList& rMarkList = GetMarkedObjectList();
1796 if (rMarkList.GetMarkCount() == 0)
1797 return;
1799 rMarkList.ForceSort();
1801 const bool bUndo = IsUndoEnabled();
1802 if( bUndo )
1804 BegUndo(SvxResId(STR_EditGroup),rMarkList.GetMarkDescription(),SdrRepeatFunc::Group);
1806 for(size_t nm = rMarkList.GetMarkCount(); nm>0; )
1808 // add UndoActions for all affected objects
1809 --nm;
1810 SdrMark* pM=rMarkList.GetMark(nm);
1811 SdrObject* pObj = pM->GetMarkedSdrObj();
1812 AddUndoActions( CreateConnectorUndo( *pObj ) );
1813 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoRemoveObject( *pObj ));
1817 SdrMarkList aNewMark;
1818 SdrPageView* pPV = GetSdrPageView();
1820 if(pPV)
1822 SdrObjList* pCurrentLst=pPV->GetObjList();
1823 SdrObjList* pSrcLst=pCurrentLst;
1824 SdrObjList* pSrcLst0=pSrcLst;
1825 // make sure OrdNums are correct
1826 if (pSrcLst->IsObjOrdNumsDirty())
1827 pSrcLst->RecalcObjOrdNums();
1828 rtl::Reference<SdrObject> pGrp;
1829 SdrObjList* pDstLst=nullptr;
1830 // if all selected objects come from foreign object lists.
1831 // the group object is the last one in the list.
1832 size_t nInsPos=pSrcLst->GetObjCount();
1833 bool bNeedInsPos=true;
1834 for (size_t nm=rMarkList.GetMarkCount(); nm>0;)
1836 --nm;
1837 SdrMark* pM=rMarkList.GetMark(nm);
1838 if (pM->GetPageView()==pPV)
1840 SdrObject* pObj=pM->GetMarkedSdrObj();
1841 if (!pGrp)
1843 pGrp = new SdrObjGroup(pObj->getSdrModelFromSdrObject());
1844 pDstLst=pGrp->GetSubList();
1845 assert(pDstLst && "Alleged group object doesn't return object list.");
1847 pSrcLst=pObj->getParentSdrObjListFromSdrObject();
1848 if (pSrcLst!=pSrcLst0)
1850 if (pSrcLst->IsObjOrdNumsDirty())
1851 pSrcLst->RecalcObjOrdNums();
1853 bool bForeignList=pSrcLst!=pCurrentLst;
1854 if (!bForeignList && bNeedInsPos)
1856 nInsPos=pObj->GetOrdNum(); // this way, all ObjOrdNum of the page are set
1857 nInsPos++;
1858 bNeedInsPos=false;
1860 pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
1861 if (!bForeignList)
1862 nInsPos--; // correct InsertPos
1863 pDstLst->InsertObject(pObj,0);
1864 GetMarkedObjectListWriteAccess().DeleteMark(nm);
1865 pSrcLst0=pSrcLst;
1868 if (pGrp!=nullptr)
1870 assert(pDstLst); // keep coverity happy
1871 aNewMark.InsertEntry(SdrMark(pGrp.get(),pPV));
1872 const size_t nCount=pDstLst->GetObjCount();
1873 pCurrentLst->InsertObject(pGrp.get(),nInsPos);
1874 if( bUndo )
1876 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // no recalculation!
1877 for (size_t no=0; no<nCount; ++no)
1879 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
1884 GetMarkedObjectListWriteAccess().Merge(aNewMark);
1885 MarkListHasChanged();
1887 if( bUndo )
1888 EndUndo();
1892 // Ungroup
1895 void SdrEditView::UnGroupMarked()
1897 SdrMarkList aNewMark;
1899 const bool bUndo = IsUndoEnabled();
1900 if( bUndo )
1901 BegUndo(u""_ustr, u""_ustr, SdrRepeatFunc::Ungroup);
1903 size_t nCount=0;
1904 OUString aName1;
1905 OUString aName;
1906 bool bNameOk=false;
1907 const SdrMarkList& rMarkList = GetMarkedObjectList();
1908 for (size_t nm=rMarkList.GetMarkCount(); nm>0;) {
1909 --nm;
1910 SdrMark* pM=rMarkList.GetMark(nm);
1911 SdrObject* pGrp=pM->GetMarkedSdrObj();
1912 SdrObjList* pSrcLst=pGrp->GetSubList();
1913 if (pSrcLst!=nullptr) {
1914 nCount++;
1915 if (nCount==1) {
1916 aName = pGrp->TakeObjNameSingul(); // retrieve name of group
1917 aName1 = pGrp->TakeObjNamePlural(); // retrieve name of group
1918 bNameOk=true;
1919 } else {
1920 if (nCount==2) aName=aName1; // set plural name
1921 if (bNameOk) {
1922 OUString aStr(pGrp->TakeObjNamePlural()); // retrieve name of group
1924 if (aStr != aName)
1925 bNameOk = false;
1928 size_t nDstCnt=pGrp->GetOrdNum();
1929 SdrObjList* pDstLst=pM->GetPageView()->GetObjList();
1930 size_t nObjCount=pSrcLst->GetObjCount();
1931 const bool bIsDiagram(pGrp->isDiagram());
1933 // If the Group is a Diagram, it has a filler BG object to guarantee
1934 // the Diagam's dimensions. Identify that shape
1935 if(bIsDiagram && nObjCount)
1937 SdrObject* pObj(pSrcLst->GetObj(0));
1939 if(nullptr != pObj && !pObj->IsGroupObject() &&
1940 !pObj->HasLineStyle() &&
1941 pObj->IsMoveProtect() && pObj->IsResizeProtect())
1943 if(pObj->HasFillStyle())
1945 // If it has FillStyle it is a useful object representing that possible
1946 // defined fill from oox import. In this case, we should remove the
1947 // Move/Resize protection to allow seamless further processing.
1949 // Undo of these is handled by SdrUndoGeoObj which holds a SdrObjGeoData,
1950 // create one
1951 if( bUndo )
1952 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
1954 pObj->SetMoveProtect(false);
1955 pObj->SetResizeProtect(false);
1957 else
1959 // If it has no FillStyle it is not useful for any further processing
1960 // but only was used as a placeholder, get directly rid of it
1961 if( bUndo )
1962 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
1964 pSrcLst->RemoveObject(0);
1966 nObjCount = pSrcLst->GetObjCount();
1971 // FIRST move contained objects to parent of group, so that
1972 // the contained objects are NOT migrated to the UNDO-ItemPool
1973 // when AddUndo(new SdrUndoDelObj(*pGrp)) is called.
1974 if( bUndo )
1976 for (size_t no=nObjCount; no>0;)
1978 no--;
1979 SdrObject* pObj=pSrcLst->GetObj(no);
1980 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoRemoveObject(*pObj));
1984 for (size_t no=0; no<nObjCount; ++no)
1986 rtl::Reference<SdrObject> pObj=pSrcLst->RemoveObject(0);
1987 pDstLst->InsertObject(pObj.get(),nDstCnt);
1988 if( bUndo )
1989 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true));
1990 nDstCnt++;
1991 // No SortCheck when inserting into MarkList, because that would
1992 // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum():
1993 aNewMark.InsertEntry(SdrMark(pObj.get(),pM->GetPageView()),false);
1996 if( bUndo )
1998 // Now it is safe to add the delete-UNDO which triggers the
1999 // MigrateItemPool now only for itself, not for the sub-objects.
2000 // nDstCnt is right, because previous inserts move group
2001 // object deeper and increase nDstCnt.
2002 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp));
2004 pDstLst->RemoveObject(nDstCnt);
2006 GetMarkedObjectListWriteAccess().DeleteMark(nm);
2009 if (nCount!=0)
2011 if (!bNameOk)
2012 aName=SvxResId(STR_ObjNamePluralGRUP); // Use the term "Group Objects," if different objects are grouped.
2013 SetUndoComment(SvxResId(STR_EditUngroup),aName);
2016 if( bUndo )
2017 EndUndo();
2019 if (nCount!=0)
2021 GetMarkedObjectListWriteAccess().Merge(aNewMark,true); // Because of the sorting above, aNewMark is reversed
2022 MarkListHasChanged();
2027 // ConvertToPoly
2030 rtl::Reference<SdrObject> SdrEditView::ImpConvertOneObj(SdrObject* pObj, bool bPath, bool bLineToArea)
2032 rtl::Reference<SdrObject> pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea);
2033 if (pNewObj)
2035 SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
2036 const bool bUndo = IsUndoEnabled();
2037 if( bUndo )
2038 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj));
2040 pOL->ReplaceObject(pNewObj.get(), pObj->GetOrdNum());
2042 return pNewObj;
2045 void SdrEditView::ImpConvertTo(bool bPath, bool bLineToArea)
2047 const SdrMarkList& rMarkList = GetMarkedObjectList();
2048 if (rMarkList.GetMarkCount() == 0)
2049 return;
2051 bool bMrkChg = false;
2052 const size_t nMarkCount=rMarkList.GetMarkCount();
2053 TranslateId pDscrID;
2054 if(bLineToArea)
2056 if(nMarkCount == 1)
2057 pDscrID = STR_EditConvToContour;
2058 else
2059 pDscrID = STR_EditConvToContours;
2061 BegUndo(SvxResId(pDscrID), rMarkList.GetMarkDescription());
2063 else
2065 if (bPath) {
2066 if (nMarkCount==1) pDscrID=STR_EditConvToCurve;
2067 else pDscrID=STR_EditConvToCurves;
2068 BegUndo(SvxResId(pDscrID),rMarkList.GetMarkDescription(),SdrRepeatFunc::ConvertToPath);
2069 } else {
2070 if (nMarkCount==1) pDscrID=STR_EditConvToPoly;
2071 else pDscrID=STR_EditConvToPolys;
2072 BegUndo(SvxResId(pDscrID),rMarkList.GetMarkDescription(),SdrRepeatFunc::ConvertToPoly);
2075 for (size_t nm=nMarkCount; nm>0;) {
2076 --nm;
2077 SdrMark* pM=rMarkList.GetMark(nm);
2078 SdrObject* pObj=pM->GetMarkedSdrObj();
2079 SdrPageView* pPV=pM->GetPageView();
2080 if (pObj->IsGroupObject() && !pObj->Is3DObj()) {
2081 SdrObject* pGrp=pObj;
2082 SdrObjListIter aIter(*pGrp, SdrIterMode::DeepNoGroups);
2083 while (aIter.IsMore()) {
2084 pObj=aIter.Next();
2085 ImpConvertOneObj(pObj,bPath,bLineToArea);
2087 } else {
2088 rtl::Reference<SdrObject> pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
2089 if (pNewObj) {
2090 bMrkChg=true;
2091 GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj.get(),pPV),nm);
2095 EndUndo();
2096 if (bMrkChg)
2098 AdjustMarkHdl();
2099 MarkListHasChanged();
2103 void SdrEditView::ConvertMarkedToPathObj(bool bLineToArea)
2105 ImpConvertTo(true, bLineToArea);
2108 void SdrEditView::ConvertMarkedToPolyObj()
2110 ImpConvertTo(false, false/*bLineToArea*/);
2113 namespace
2115 GDIMetaFile GetMetaFile(SdrGrafObj const * pGraf)
2117 if (pGraf->HasGDIMetaFile())
2118 return pGraf->GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
2119 assert(pGraf->isEmbeddedVectorGraphicData());
2120 return pGraf->getMetafileFromEmbeddedVectorGraphicData();
2124 // Metafile Import
2125 void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo)
2127 const bool bUndo = IsUndoEnabled();
2129 if( bUndo )
2130 BegUndo(u""_ustr, u""_ustr, SdrRepeatFunc::ImportMtf);
2132 const SdrMarkList& rMarkList = GetMarkedObjectList();
2133 rMarkList.ForceSort();
2134 SdrMarkList aForTheDescription;
2135 SdrMarkList aNewMarked;
2136 for (size_t nm =rMarkList.GetMarkCount(); nm > 0; )
2138 // create Undo objects for all new objects
2139 // check for cancellation between the metafiles
2140 if (pProgrInfo != nullptr)
2142 pProgrInfo->SetNextObject();
2143 if (!pProgrInfo->ReportActions(0))
2144 break;
2147 --nm;
2148 SdrMark* pM=rMarkList.GetMark(nm);
2149 SdrObject* pObj=pM->GetMarkedSdrObj();
2150 SdrPageView* pPV=pM->GetPageView();
2151 SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
2152 const size_t nInsPos=pObj->GetOrdNum()+1;
2153 size_t nInsCnt=0;
2154 tools::Rectangle aLogicRect;
2156 SdrGrafObj* pGraf = dynamic_cast<SdrGrafObj*>( pObj );
2157 if (pGraf != nullptr)
2159 Graphic aGraphic = pGraf->GetGraphic();
2160 auto const & pVectorGraphicData = aGraphic.getVectorGraphicData();
2162 if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Pdf)
2164 auto pPdfium = vcl::pdf::PDFiumLibrary::get();
2165 if (pPdfium)
2167 aLogicRect = pGraf->GetLogicRect();
2168 ImpSdrPdfImport aFilter(GetModel(), pObj->GetLayer(), aLogicRect, aGraphic);
2169 if (aGraphic.getPageNumber() < aFilter.GetPageCount())
2171 nInsCnt = aFilter.DoImport(*pOL, nInsPos, aGraphic.getPageNumber(), pProgrInfo);
2175 else if (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedVectorGraphicData() )
2177 GDIMetaFile aMetaFile(GetMetaFile(pGraf));
2178 if (aMetaFile.GetActionSize())
2180 aLogicRect = pGraf->GetLogicRect();
2181 ImpSdrGDIMetaFileImport aFilter(GetModel(), pObj->GetLayer(), aLogicRect);
2182 nInsCnt = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo);
2187 SdrOle2Obj* pOle2 = dynamic_cast<SdrOle2Obj*>(pObj);
2188 if (pOle2)
2190 if (const Graphic* pGraphic = pOle2->GetGraphic())
2192 aLogicRect = pOle2->GetLogicRect();
2193 ImpSdrGDIMetaFileImport aFilter(GetModel(), pObj->GetLayer(), aLogicRect);
2194 nInsCnt = aFilter.DoImport(pGraphic->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo);
2198 if (nInsCnt != 0)
2200 // transformation
2201 GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat());
2202 size_t nObj = nInsPos;
2204 if (aGeoStat.m_nShearAngle)
2205 aGeoStat.RecalcTan();
2207 if (aGeoStat.m_nRotationAngle)
2208 aGeoStat.RecalcSinCos();
2210 for (size_t i = 0; i < nInsCnt; i++)
2212 if (bUndo)
2213 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));
2215 // update new MarkList
2216 SdrObject* pCandidate = pOL->GetObj(nObj);
2218 // apply original transformation
2219 if (aGeoStat.m_nShearAngle)
2220 pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
2222 if (aGeoStat.m_nRotationAngle)
2223 pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
2225 SdrMark aNewMark(pCandidate, pPV);
2226 aNewMarked.InsertEntry(aNewMark);
2228 nObj++;
2231 aForTheDescription.InsertEntry(*pM);
2233 if (bUndo)
2234 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
2236 // remove object from selection and delete
2237 GetMarkedObjectListWriteAccess().DeleteMark(rMarkList.FindObject(pObj));
2238 pOL->RemoveObject(nInsPos-1);
2242 if (aNewMarked.GetMarkCount())
2244 // create new selection
2245 for (size_t a = 0; a < aNewMarked.GetMarkCount(); ++a)
2247 GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
2250 rMarkList.ForceSort();
2253 if (bUndo)
2255 SetUndoComment(SvxResId(STR_EditImportMtf),aForTheDescription.GetMarkDescription());
2256 EndUndo();
2260 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */