Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/svdedtv2.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
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>
55
56
using namespace com::sun::star;
57
58
SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const
59
0
{
60
0
  return nullptr;
61
0
}
62
63
SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const
64
0
{
65
0
  return nullptr;
66
0
}
67
68
void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, size_t /*nOldPos*/, size_t /*nNewPos*/)
69
0
{
70
0
}
71
72
void SdrEditView::MovMarkedToTop()
73
0
{
74
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
75
0
    const size_t nCount=rMarkList.GetMarkCount();
76
0
    if (nCount==0)
77
0
        return;
78
79
0
    const bool bUndo = IsUndoEnabled();
80
81
0
    if( bUndo )
82
0
        BegUndo(SvxResId(STR_EditMovToTop),rMarkList.GetMarkDescription(),SdrRepeatFunc::MoveToTop);
83
84
0
    rMarkList.ForceSort();
85
0
    for (size_t nm=0; nm<nCount; ++nm)
86
0
    { // All Ordnums have to be correct!
87
0
        rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
88
0
    }
89
0
    bool bChg=false;
90
0
    SdrObjList* pOL0=nullptr;
91
0
    size_t nNewPos=0;
92
0
    for (size_t nm=nCount; nm>0;)
93
0
    {
94
0
        --nm;
95
0
        SdrMark* pM=rMarkList.GetMark(nm);
96
0
        SdrObject* pObj=pM->GetMarkedSdrObj();
97
0
        SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
98
0
        if (pOL!=pOL0)
99
0
        {
100
0
            nNewPos = pOL->GetObjCount()-1;
101
0
            pOL0=pOL;
102
0
        }
103
0
        const size_t nNowPos = pObj->GetOrdNumDirect();
104
0
        const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
105
0
        size_t nCmpPos = nNowPos+1;
106
0
        SdrObject* pMaxObj=GetMaxToTopObj(pObj);
107
0
        if (pMaxObj!=nullptr)
108
0
        {
109
0
            size_t nMaxPos=pMaxObj->GetOrdNum();
110
0
            if (nMaxPos!=0)
111
0
                nMaxPos--;
112
0
            if (nNewPos>nMaxPos)
113
0
                nNewPos=nMaxPos; // neither go faster...
114
0
            if (nNewPos<nNowPos)
115
0
                nNewPos=nNowPos; // nor go in the other direction
116
0
        }
117
0
        bool bEnd=false;
118
0
        while (nCmpPos<nNewPos && !bEnd)
119
0
        {
120
0
            assert(pOL);
121
0
            SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
122
0
            if (pCmpObj==nullptr)
123
0
            {
124
0
                OSL_FAIL("MovMarkedToTop(): Reference object not found.");
125
0
                bEnd=true;
126
0
            }
127
0
            else if (pCmpObj==pMaxObj)
128
0
            {
129
0
                nNewPos=nCmpPos;
130
0
                nNewPos--;
131
0
                bEnd=true;
132
0
            }
133
0
            else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
134
0
            {
135
0
                nNewPos=nCmpPos;
136
0
                bEnd=true;
137
0
            }
138
0
            else
139
0
            {
140
0
                nCmpPos++;
141
0
            }
142
0
        }
143
0
        if (nNowPos!=nNewPos)
144
0
        {
145
0
            bChg=true;
146
0
            pOL->SetObjectOrdNum(nNowPos,nNewPos);
147
0
            if( bUndo )
148
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
149
0
            ObjOrderChanged(pObj,nNowPos,nNewPos);
150
0
        }
151
0
        nNewPos--;
152
0
    }
153
154
0
    if( bUndo )
155
0
        EndUndo();
156
157
0
    if (bChg)
158
0
        MarkListHasChanged();
159
0
}
160
161
void SdrEditView::MovMarkedToBtm()
162
0
{
163
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
164
0
    const size_t nCount=rMarkList.GetMarkCount();
165
0
    if (nCount==0)
166
0
        return;
167
168
0
    const bool bUndo = IsUndoEnabled();
169
170
0
    if( bUndo )
171
0
        BegUndo(SvxResId(STR_EditMovToBtm),rMarkList.GetMarkDescription(),SdrRepeatFunc::MoveToBottom);
172
173
0
    rMarkList.ForceSort();
174
0
    for (size_t nm=0; nm<nCount; ++nm)
175
0
    { // All Ordnums have to be correct!
176
0
        rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
177
0
    }
178
179
0
    bool bChg=false;
180
0
    SdrObjList* pOL0=nullptr;
181
0
    size_t nNewPos=0;
182
0
    for (size_t nm=0; nm<nCount; ++nm)
183
0
    {
184
0
        SdrMark* pM=rMarkList.GetMark(nm);
185
0
        SdrObject* pObj=pM->GetMarkedSdrObj();
186
0
        SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
187
0
        if (pOL!=pOL0)
188
0
        {
189
0
            nNewPos=0;
190
0
            pOL0=pOL;
191
0
        }
192
0
        const size_t nNowPos = pObj->GetOrdNumDirect();
193
0
        const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
194
0
        size_t nCmpPos = nNowPos;
195
0
        if (nCmpPos>0)
196
0
            --nCmpPos;
197
0
        SdrObject* pMaxObj=GetMaxToBtmObj(pObj);
198
0
        if (pMaxObj!=nullptr)
199
0
        {
200
0
            const size_t nMinPos=pMaxObj->GetOrdNum()+1;
201
0
            if (nNewPos<nMinPos)
202
0
                nNewPos=nMinPos; // neither go faster...
203
0
            if (nNewPos>nNowPos)
204
0
                nNewPos=nNowPos; // nor go in the other direction
205
0
        }
206
0
        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
0
        while (nCmpPos>nNewPos && !bEnd)
211
0
        {
212
0
            assert(pOL);
213
0
            SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
214
0
            if (pCmpObj==nullptr)
215
0
            {
216
0
                OSL_FAIL("MovMarkedToBtm(): Reference object not found.");
217
0
                bEnd=true;
218
0
            }
219
0
            else if (pCmpObj==pMaxObj)
220
0
            {
221
0
                nNewPos=nCmpPos;
222
0
                nNewPos++;
223
0
                bEnd=true;
224
0
            }
225
0
            else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
226
0
            {
227
0
                nNewPos=nCmpPos;
228
0
                bEnd=true;
229
0
            }
230
0
            else
231
0
            {
232
0
                nCmpPos--;
233
0
            }
234
0
        }
235
0
        if (nNowPos!=nNewPos)
236
0
        {
237
0
            bChg=true;
238
0
            pOL->SetObjectOrdNum(nNowPos,nNewPos);
239
0
            if( bUndo )
240
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
241
0
            ObjOrderChanged(pObj,nNowPos,nNewPos);
242
0
        }
243
0
        nNewPos++;
244
0
    }
245
246
0
    if(bUndo)
247
0
        EndUndo();
248
249
0
    if(bChg)
250
0
        MarkListHasChanged();
251
0
}
252
253
void SdrEditView::PutMarkedToTop()
254
0
{
255
0
    PutMarkedInFrontOfObj(nullptr);
256
0
}
257
258
void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj)
259
0
{
260
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
261
0
    const size_t nCount=rMarkList.GetMarkCount();
262
0
    if (nCount==0)
263
0
        return;
264
265
0
    const bool bUndo = IsUndoEnabled();
266
0
    if( bUndo )
267
0
        BegUndo(SvxResId(STR_EditPutToTop),rMarkList.GetMarkDescription(),SdrRepeatFunc::PutToTop);
268
269
0
    rMarkList.ForceSort();
270
271
0
    if (pRefObj!=nullptr)
272
0
    {
273
        // Make "in front of the object" work, even if the
274
        // selected objects are already in front of the other object
275
0
        const size_t nRefMark=rMarkList.FindObject(pRefObj);
276
0
        SdrMark aRefMark;
277
0
        if (nRefMark!=SAL_MAX_SIZE)
278
0
        {
279
0
            aRefMark=*rMarkList.GetMark(nRefMark);
280
0
            GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
281
0
        }
282
0
        PutMarkedToBtm();
283
0
        if (nRefMark!=SAL_MAX_SIZE)
284
0
        {
285
0
            GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
286
0
            rMarkList.ForceSort();
287
0
        }
288
0
    }
289
0
    for (size_t nm=0; nm<nCount; ++nm)
290
0
    { // All Ordnums have to be correct!
291
0
        rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
292
0
    }
293
0
    bool bChg=false;
294
0
    SdrObjList* pOL0=nullptr;
295
0
    size_t nNewPos=0;
296
0
    for (size_t nm=nCount; nm>0;)
297
0
    {
298
0
        --nm;
299
0
        SdrMark* pM=rMarkList.GetMark(nm);
300
0
        SdrObject* pObj=pM->GetMarkedSdrObj();
301
0
        if (pObj!=pRefObj)
302
0
        {
303
0
            SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
304
0
            if (pOL!=pOL0)
305
0
            {
306
0
                nNewPos=pOL->GetObjCount()-1;
307
0
                pOL0=pOL;
308
0
            }
309
0
            const size_t nNowPos=pObj->GetOrdNumDirect();
310
0
            SdrObject* pMaxObj=GetMaxToTopObj(pObj);
311
0
            if (pMaxObj!=nullptr)
312
0
            {
313
0
                size_t nMaxOrd=pMaxObj->GetOrdNum(); // sadly doesn't work any other way
314
0
                if (nMaxOrd>0)
315
0
                    nMaxOrd--;
316
0
                if (nNewPos>nMaxOrd)
317
0
                    nNewPos=nMaxOrd; // neither go faster...
318
0
                if (nNewPos<nNowPos)
319
0
                    nNewPos=nNowPos; // nor go into the other direction
320
0
            }
321
0
            if (pRefObj!=nullptr)
322
0
            {
323
0
                if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject())
324
0
                {
325
0
                    const size_t nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way
326
0
                    if (nNewPos>nMaxOrd)
327
0
                        nNewPos=nMaxOrd; // neither go faster...
328
0
                    if (nNewPos<nNowPos)
329
0
                        nNewPos=nNowPos; // nor go into the other direction
330
0
                }
331
0
                else
332
0
                {
333
0
                    nNewPos=nNowPos; // different PageView, so don't change
334
0
                }
335
0
            }
336
0
            if (nNowPos!=nNewPos)
337
0
            {
338
0
                bChg=true;
339
0
                pOL->SetObjectOrdNum(nNowPos,nNewPos);
340
0
                if( bUndo )
341
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
342
0
                ObjOrderChanged(pObj,nNowPos,nNewPos);
343
0
            }
344
0
            nNewPos--;
345
0
        } // if (pObj!=pRefObj)
346
0
    } // for loop over all selected objects
347
348
0
    if( bUndo )
349
0
        EndUndo();
350
351
0
    if(bChg)
352
0
        MarkListHasChanged();
353
0
}
354
355
void SdrEditView::PutMarkedToBtm()
356
0
{
357
0
    PutMarkedBehindObj(nullptr);
358
0
}
359
360
void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj)
361
0
{
362
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
363
0
    const size_t nCount=rMarkList.GetMarkCount();
364
0
    if (nCount==0)
365
0
        return;
366
367
0
    const bool bUndo = IsUndoEnabled();
368
369
0
    if( bUndo )
370
0
        BegUndo(SvxResId(STR_EditPutToBtm),rMarkList.GetMarkDescription(),SdrRepeatFunc::PutToBottom);
371
372
0
    rMarkList.ForceSort();
373
0
    if (pRefObj!=nullptr)
374
0
    {
375
        // Make "behind the object" work, even if the
376
        // selected objects are already behind the other object
377
0
        const size_t nRefMark=rMarkList.FindObject(pRefObj);
378
0
        SdrMark aRefMark;
379
0
        if (nRefMark!=SAL_MAX_SIZE)
380
0
        {
381
0
            aRefMark=*rMarkList.GetMark(nRefMark);
382
0
            GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
383
0
        }
384
0
        PutMarkedToTop();
385
0
        if (nRefMark!=SAL_MAX_SIZE)
386
0
        {
387
0
            GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
388
0
            rMarkList.ForceSort();
389
0
        }
390
0
    }
391
0
    for (size_t nm=0; nm<nCount; ++nm) { // All Ordnums have to be correct!
392
0
        rMarkList.GetMark(nm)->GetMarkedSdrObj()->GetOrdNum();
393
0
    }
394
0
    bool bChg=false;
395
0
    SdrObjList* pOL0=nullptr;
396
0
    size_t nNewPos=0;
397
0
    for (size_t nm=0; nm<nCount; ++nm) {
398
0
        SdrMark* pM=rMarkList.GetMark(nm);
399
0
        SdrObject* pObj=pM->GetMarkedSdrObj();
400
0
        if (pObj!=pRefObj) {
401
0
            SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
402
0
            if (pOL!=pOL0) {
403
0
                nNewPos=0;
404
0
                pOL0=pOL;
405
0
            }
406
0
            const size_t nNowPos=pObj->GetOrdNumDirect();
407
0
            SdrObject* pMinObj=GetMaxToBtmObj(pObj);
408
0
            if (pMinObj!=nullptr) {
409
0
                const size_t nMinOrd=pMinObj->GetOrdNum()+1; // sadly doesn't work any differently
410
0
                if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
411
0
                if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
412
0
            }
413
0
            if (pRefObj!=nullptr) {
414
0
                if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject()) {
415
0
                    const size_t nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently
416
0
                    if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
417
0
                    if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
418
0
                } else {
419
0
                    nNewPos=nNowPos; // different PageView, so don't change
420
0
                }
421
0
            }
422
0
            if (nNowPos!=nNewPos) {
423
0
                bChg=true;
424
0
                pOL->SetObjectOrdNum(nNowPos,nNewPos);
425
0
                if( bUndo )
426
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
427
0
                ObjOrderChanged(pObj,nNowPos,nNewPos);
428
0
            }
429
0
            nNewPos++;
430
0
        } // if (pObj!=pRefObj)
431
0
    } // for loop over all selected objects
432
433
0
    if(bUndo)
434
0
        EndUndo();
435
436
0
    if(bChg)
437
0
        MarkListHasChanged();
438
439
0
}
440
441
void SdrEditView::ReverseOrderOfMarked()
442
0
{
443
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
444
0
    rMarkList.ForceSort();
445
0
    const size_t nMarkCount=rMarkList.GetMarkCount();
446
0
    if (nMarkCount<=0)
447
0
        return;
448
449
0
    bool bChg=false;
450
451
0
    bool bUndo = IsUndoEnabled();
452
0
    if( bUndo )
453
0
        BegUndo(SvxResId(STR_EditRevOrder),rMarkList.GetMarkDescription(),SdrRepeatFunc::ReverseOrder);
454
455
0
    size_t a=0;
456
0
    do {
457
        // take into account selection across multiple PageViews
458
0
        size_t b=a+1;
459
0
        while (b<nMarkCount && rMarkList.GetMark(b)->GetPageView() == rMarkList.GetMark(a)->GetPageView()) ++b;
460
0
        --b;
461
0
        SdrObjList* pOL=rMarkList.GetMark(a)->GetPageView()->GetObjList();
462
0
        size_t c=b;
463
0
        if (a<c) { // make sure OrdNums aren't dirty
464
0
            rMarkList.GetMark(a)->GetMarkedSdrObj()->GetOrdNum();
465
0
        }
466
0
        while (a<c) {
467
0
            SdrObject* pObj1=rMarkList.GetMark(a)->GetMarkedSdrObj();
468
0
            SdrObject* pObj2=rMarkList.GetMark(c)->GetMarkedSdrObj();
469
0
            const size_t nOrd1=pObj1->GetOrdNumDirect();
470
0
            const size_t nOrd2=pObj2->GetOrdNumDirect();
471
0
            if( bUndo )
472
0
            {
473
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2));
474
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1));
475
0
            }
476
0
            pOL->SetObjectOrdNum(nOrd1,nOrd2);
477
            // Obj 2 has moved forward by one position, so now nOrd2-1
478
0
            pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
479
            // use Replace instead of SetOrdNum for performance reasons (recalculation of Ordnums)
480
0
            ++a;
481
0
            --c;
482
0
            bChg=true;
483
0
        }
484
0
        a=b+1;
485
0
    } while (a<nMarkCount);
486
487
0
    if(bUndo)
488
0
        EndUndo();
489
490
0
    if(bChg)
491
0
        MarkListHasChanged();
492
0
}
493
494
void SdrEditView::ImpCheckToTopBtmPossible()
495
0
{
496
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
497
0
    const size_t nCount=rMarkList.GetMarkCount();
498
0
    if (nCount==0)
499
0
        return;
500
0
    if (nCount==1)
501
0
    { // special-casing for single selection
502
0
        SdrObject* pObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
503
0
        SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
504
0
        SAL_WARN_IF(!pOL, "svx", "Object somehow has no ObjList");
505
0
        size_t nMax = pOL ? pOL->GetObjCount() : 0;
506
0
        size_t nMin = 0;
507
0
        const size_t nObjNum=pObj->GetOrdNum();
508
0
        SdrObject* pRestrict=GetMaxToTopObj(pObj);
509
0
        if (pRestrict!=nullptr) {
510
0
            const size_t nRestrict=pRestrict->GetOrdNum();
511
0
            if (nRestrict<nMax) nMax=nRestrict;
512
0
        }
513
0
        pRestrict=GetMaxToBtmObj(pObj);
514
0
        if (pRestrict!=nullptr) {
515
0
            const size_t nRestrict=pRestrict->GetOrdNum();
516
0
            if (nRestrict>nMin) nMin=nRestrict;
517
0
        }
518
0
        m_bToTopPossible = nObjNum+1 < nMax;
519
0
        m_bToBtmPossible = nObjNum > nMin;
520
0
    } else { // multiple selection
521
0
        SdrObjList* pOL0=nullptr;
522
0
        size_t nPos0 = 0;
523
0
        for (size_t nm = 0; !m_bToBtmPossible && nm<nCount; ++nm) { // check 'send to background'
524
0
            SdrObject* pObj=rMarkList.GetMark(nm)->GetMarkedSdrObj();
525
0
            SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
526
0
            if (pOL!=pOL0) {
527
0
                nPos0 = 0;
528
0
                pOL0=pOL;
529
0
            }
530
0
            const size_t nPos = pObj->GetOrdNum();
531
0
            m_bToBtmPossible = nPos && (nPos-1 > nPos0);
532
0
            nPos0 = nPos;
533
0
        }
534
535
0
        pOL0=nullptr;
536
0
        nPos0 = SAL_MAX_SIZE;
537
0
        for (size_t nm=nCount; !m_bToTopPossible && nm>0; ) { // check 'bring to front'
538
0
            --nm;
539
0
            SdrObject* pObj=rMarkList.GetMark(nm)->GetMarkedSdrObj();
540
0
            SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
541
0
            if (pOL!=pOL0) {
542
0
                nPos0=pOL->GetObjCount();
543
0
                pOL0=pOL;
544
0
            }
545
0
            const size_t nPos = pObj->GetOrdNum();
546
0
            m_bToTopPossible = nPos+1 < nPos0;
547
0
            nPos0=nPos;
548
0
        }
549
0
    }
550
0
}
551
552
553
// Combine
554
555
556
void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
557
0
{
558
0
    if (pSource!=nullptr) {
559
0
        SdrObjList* pOL=pSource->GetSubList();
560
0
        if (pOL!=nullptr && !pSource->Is3DObj()) { // get first non-group object from group
561
0
            SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
562
0
            pSource=aIter.Next();
563
0
        }
564
0
    }
565
566
0
    if(!(pSource && pDest))
567
0
        return;
568
569
0
    SfxItemSetFixed<SDRATTR_START, SDRATTR_NOTPERSIST_FIRST-1,
570
0
        SDRATTR_NOTPERSIST_LAST+1, SDRATTR_END,
571
0
        EE_ITEMS_START, EE_ITEMS_END> aSet(GetModel().GetItemPool());
572
573
0
    aSet.Put(pSource->GetMergedItemSet());
574
575
0
    pDest->ClearMergedItem();
576
0
    pDest->SetMergedItemSet(aSet);
577
578
0
    pDest->NbcSetLayer(pSource->GetLayer());
579
0
    pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), true);
580
0
}
581
582
bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj)
583
0
{
584
    // new condition IsLine() to be able to combine simple Lines
585
0
    bool bIsLine(false);
586
587
0
    const SdrPathObj* pPath = dynamic_cast< const SdrPathObj*>( pObj );
588
589
0
    if(pPath)
590
0
    {
591
0
        bIsLine = pPath->IsLine();
592
0
    }
593
594
0
    SdrObjTransformInfoRec aInfo;
595
0
    pObj->TakeObjInfo(aInfo);
596
597
0
    return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine);
598
0
}
599
600
bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj)
601
0
{
602
0
    SdrObjList* pOL = pObj->GetSubList();
603
604
0
    if(pOL && !pObj->Is3DObj())
605
0
    {
606
0
        SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
607
608
0
        while(aIter.IsMore())
609
0
        {
610
0
            SdrObject* pObj1 = aIter.Next();
611
612
            // all members of a group have to be convertible
613
0
            if(!ImpCanConvertForCombine1(pObj1))
614
0
            {
615
0
                return false;
616
0
            }
617
0
        }
618
0
    }
619
0
    else
620
0
    {
621
0
        if(!ImpCanConvertForCombine1(pObj))
622
0
        {
623
0
            return false;
624
0
        }
625
0
    }
626
627
0
    return true;
628
0
}
629
630
basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj)
631
0
{
632
0
    basegfx::B2DPolyPolygon aRetval;
633
0
    const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj );
634
635
0
    if(pPath && !pObj->GetOutlinerParaObject())
636
0
    {
637
0
        aRetval = pPath->GetPathPoly();
638
0
    }
639
0
    else
640
0
    {
641
0
        rtl::Reference<SdrObject> pConvObj = pObj->ConvertToPolyObj(true/*bCombine*/, false);
642
643
0
        if(pConvObj)
644
0
        {
645
0
            SdrObjList* pOL = pConvObj->GetSubList();
646
647
0
            if(pOL)
648
0
            {
649
0
                SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
650
651
0
                while(aIter.IsMore())
652
0
                {
653
0
                    SdrObject* pObj1 = aIter.Next();
654
0
                    pPath = dynamic_cast<SdrPathObj*>( pObj1 );
655
656
0
                    if(pPath)
657
0
                    {
658
0
                        aRetval.append(pPath->GetPathPoly());
659
0
                    }
660
0
                }
661
0
            }
662
0
            else
663
0
            {
664
0
                pPath = dynamic_cast<SdrPathObj*>( pConvObj.get() );
665
666
0
                if(pPath)
667
0
                {
668
0
                    aRetval = pPath->GetPathPoly();
669
0
                }
670
0
            }
671
0
        }
672
0
    }
673
674
0
    return aRetval;
675
0
}
676
677
basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj)
678
0
{
679
0
    SdrObjList* pOL = pObj->GetSubList();
680
681
0
    if(pOL && !pObj->Is3DObj())
682
0
    {
683
0
        basegfx::B2DPolyPolygon aRetval;
684
0
        SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
685
686
0
        while(aIter.IsMore())
687
0
        {
688
0
            SdrObject* pObj1 = aIter.Next();
689
0
            aRetval.append(ImpGetPolyPolygon1(pObj1));
690
0
        }
691
692
0
        return aRetval;
693
0
    }
694
0
    else
695
0
    {
696
0
        return ImpGetPolyPolygon1(pObj);
697
0
    }
698
0
}
699
700
basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
701
0
{
702
0
    const sal_uInt32 nPolyCount(rPolyPolygon.count());
703
704
0
    if(0 == nPolyCount)
705
0
    {
706
0
        return basegfx::B2DPolygon();
707
0
    }
708
0
    else if(1 == nPolyCount)
709
0
    {
710
0
        return rPolyPolygon.getB2DPolygon(0);
711
0
    }
712
0
    else
713
0
    {
714
0
        basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0));
715
716
0
        for(sal_uInt32 a(1); a < nPolyCount; a++)
717
0
        {
718
0
            basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
719
720
0
            if(aRetval.count())
721
0
            {
722
0
                if(aCandidate.count())
723
0
                {
724
0
                    const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0));
725
0
                    const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1));
726
0
                    const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0));
727
0
                    const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1));
728
729
0
                    const double fRACA(basegfx::B2DVector(aCA - aRA).getLength());
730
0
                    const double fRACB(basegfx::B2DVector(aCB - aRA).getLength());
731
0
                    const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength());
732
0
                    const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength());
733
734
0
                    const double fSmallestRA(std::min(fRACA, fRACB));
735
0
                    const double fSmallestRB(std::min(fRBCA, fRBCB));
736
737
0
                    if(fSmallestRA < fSmallestRB)
738
0
                    {
739
                        // flip result
740
0
                        aRetval.flip();
741
0
                    }
742
743
0
                    const double fSmallestCA(std::min(fRACA, fRBCA));
744
0
                    const double fSmallestCB(std::min(fRACB, fRBCB));
745
746
0
                    if(fSmallestCB < fSmallestCA)
747
0
                    {
748
                        // flip candidate
749
0
                        aCandidate.flip();
750
0
                    }
751
752
                    // append candidate to retval
753
0
                    aRetval.append(aCandidate);
754
0
                }
755
0
            }
756
0
            else
757
0
            {
758
0
                aRetval = std::move(aCandidate);
759
0
            }
760
0
        }
761
762
0
        return aRetval;
763
0
    }
764
0
}
765
766
namespace {
767
768
// for distribution dialog function
769
struct ImpDistributeEntry
770
{
771
    SdrObject*                  mpObj;
772
    sal_Int32                       mnPos;
773
    sal_Int32                       mnLength;
774
};
775
776
}
777
778
typedef std::vector<ImpDistributeEntry> ImpDistributeEntryList;
779
780
void SdrEditView::DistributeMarkedObjects(sal_uInt16 SlotID)
781
0
{
782
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
783
0
    const size_t nMark(rMarkList.GetMarkCount());
784
785
0
    if(nMark <= 2)
786
0
        return;
787
788
0
    SvxDistributeHorizontal eHor = SvxDistributeHorizontal::NONE;
789
0
    SvxDistributeVertical eVer = SvxDistributeVertical::NONE;
790
791
0
    switch (SlotID)
792
0
    {
793
0
        case SID_DISTRIBUTE_HLEFT: eHor = SvxDistributeHorizontal::Left; break;
794
0
        case SID_DISTRIBUTE_HCENTER: eHor = SvxDistributeHorizontal::Center; break;
795
0
        case SID_DISTRIBUTE_HDISTANCE: eHor = SvxDistributeHorizontal::Distance; break;
796
0
        case SID_DISTRIBUTE_HRIGHT: eHor = SvxDistributeHorizontal::Right; break;
797
0
        case SID_DISTRIBUTE_VTOP: eVer = SvxDistributeVertical::Top; break;
798
0
        case SID_DISTRIBUTE_VCENTER: eVer = SvxDistributeVertical::Center; break;
799
0
        case SID_DISTRIBUTE_VDISTANCE: eVer = SvxDistributeVertical::Distance; break;
800
0
        case SID_DISTRIBUTE_VBOTTOM: eVer = SvxDistributeVertical::Bottom; break;
801
0
    }
802
803
0
    ImpDistributeEntryList aEntryList;
804
0
    ImpDistributeEntryList::iterator itEntryList;
805
0
    sal_uInt32 nFullLength;
806
807
0
    const bool bUndo = IsUndoEnabled();
808
0
    if( bUndo )
809
0
        BegUndo();
810
811
0
    if(eHor != SvxDistributeHorizontal::NONE)
812
0
    {
813
        // build sorted entry list
814
0
        nFullLength = 0;
815
816
0
        for( size_t a = 0; a < nMark; ++a )
817
0
        {
818
0
            SdrMark* pMark = rMarkList.GetMark(a);
819
0
            ImpDistributeEntry aNew;
820
821
0
            aNew.mpObj = pMark->GetMarkedSdrObj();
822
823
0
            switch(eHor)
824
0
            {
825
0
                case SvxDistributeHorizontal::Left:
826
0
                {
827
0
                    aNew.mnPos = aNew.mpObj->GetSnapRect().Left();
828
0
                    break;
829
0
                }
830
0
                case SvxDistributeHorizontal::Center:
831
0
                {
832
0
                    aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
833
0
                    break;
834
0
                }
835
0
                case SvxDistributeHorizontal::Distance:
836
0
                {
837
0
                    aNew.mnLength = aNew.mpObj->GetSnapRect().GetWidth() + 1;
838
0
                    nFullLength += aNew.mnLength;
839
0
                    aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
840
0
                    break;
841
0
                }
842
0
                case SvxDistributeHorizontal::Right:
843
0
                {
844
0
                    aNew.mnPos = aNew.mpObj->GetSnapRect().Right();
845
0
                    break;
846
0
                }
847
0
                default: break;
848
0
            }
849
850
0
            itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
851
0
                [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
852
0
            if ( itEntryList < aEntryList.end() )
853
0
                aEntryList.insert( itEntryList, aNew );
854
0
            else
855
0
                aEntryList.push_back( aNew );
856
0
        }
857
858
0
        if(eHor == SvxDistributeHorizontal::Distance)
859
0
        {
860
            // calculate room in-between
861
0
            sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1;
862
0
            double fStepWidth = (static_cast<double>(nWidth) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
863
0
            double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
864
0
            fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
865
866
            // move entries 1..n-1
867
0
            for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i )
868
0
            {
869
0
                ImpDistributeEntry& rCurr = aEntryList[ i    ];
870
0
                ImpDistributeEntry& rNext = aEntryList[ i + 1];
871
0
                sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
872
0
                if( bUndo )
873
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
874
0
                rCurr.mpObj->Move(Size(nDelta, 0));
875
0
                fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
876
0
            }
877
0
        }
878
0
        else
879
0
        {
880
            // calculate distances
881
0
            sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
882
0
            double fStepWidth = static_cast<double>(nWidth) / static_cast<double>(aEntryList.size() - 1);
883
0
            double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
884
0
            fStepStart += fStepWidth;
885
886
            // move entries 1..n-1
887
0
            for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i )
888
0
            {
889
0
                ImpDistributeEntry& rCurr = aEntryList[ i ];
890
0
                sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
891
0
                if( bUndo )
892
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
893
0
                rCurr.mpObj->Move(Size(nDelta, 0));
894
0
                fStepStart += fStepWidth;
895
0
            }
896
0
        }
897
898
        // clear list
899
0
        aEntryList.clear();
900
0
    }
901
902
0
    if(eVer != SvxDistributeVertical::NONE)
903
0
    {
904
        // build sorted entry list
905
0
        nFullLength = 0;
906
907
0
        for( size_t a = 0; a < nMark; ++a )
908
0
        {
909
0
            SdrMark* pMark = rMarkList.GetMark(a);
910
0
            ImpDistributeEntry aNew;
911
912
0
            aNew.mpObj = pMark->GetMarkedSdrObj();
913
914
0
            switch(eVer)
915
0
            {
916
0
                case SvxDistributeVertical::Top:
917
0
                {
918
0
                    aNew.mnPos = aNew.mpObj->GetSnapRect().Top();
919
0
                    break;
920
0
                }
921
0
                case SvxDistributeVertical::Center:
922
0
                {
923
0
                    aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
924
0
                    break;
925
0
                }
926
0
                case SvxDistributeVertical::Distance:
927
0
                {
928
0
                    aNew.mnLength = aNew.mpObj->GetSnapRect().GetHeight() + 1;
929
0
                    nFullLength += aNew.mnLength;
930
0
                    aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
931
0
                    break;
932
0
                }
933
0
                case SvxDistributeVertical::Bottom:
934
0
                {
935
0
                    aNew.mnPos = aNew.mpObj->GetSnapRect().Bottom();
936
0
                    break;
937
0
                }
938
0
                default: break;
939
0
            }
940
941
0
            itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
942
0
                [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
943
0
            if ( itEntryList < aEntryList.end() )
944
0
                aEntryList.insert( itEntryList, aNew );
945
0
            else
946
0
                aEntryList.push_back( aNew );
947
0
        }
948
949
0
        if(eVer == SvxDistributeVertical::Distance)
950
0
        {
951
            // calculate room in-between
952
0
            sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1;
953
0
            double fStepWidth = (static_cast<double>(nHeight) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
954
0
            double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
955
0
            fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
956
957
            // move entries 1..n-1
958
0
            for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
959
0
            {
960
0
                ImpDistributeEntry& rCurr = aEntryList[ i     ];
961
0
                ImpDistributeEntry& rNext = aEntryList[ i + 1 ];
962
0
                sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
963
0
                if( bUndo )
964
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
965
0
                rCurr.mpObj->Move(Size(0, nDelta));
966
0
                fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
967
0
            }
968
0
        }
969
0
        else
970
0
        {
971
            // calculate distances
972
0
            sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
973
0
            double fStepWidth = static_cast<double>(nHeight) / static_cast<double>(aEntryList.size() - 1);
974
0
            double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
975
0
            fStepStart += fStepWidth;
976
977
            // move entries 1..n-1
978
0
            for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
979
0
            {
980
0
                ImpDistributeEntry& rCurr = aEntryList[ i ];
981
0
                sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
982
0
                if( bUndo )
983
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
984
0
                rCurr.mpObj->Move(Size(0, nDelta));
985
0
                fStepStart += fStepWidth;
986
0
            }
987
0
        }
988
989
        // clear list
990
0
        aEntryList.clear();
991
0
    }
992
993
    // UNDO-Comment and end of UNDO
994
0
    GetModel().SetUndoComment(SvxResId(STR_DistributeMarkedObjects));
995
996
0
    if( bUndo )
997
0
        EndUndo();
998
0
}
999
1000
void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode)
1001
0
{
1002
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1003
    // #i73441# check content
1004
0
    if(rMarkList.GetMarkCount() == 0)
1005
0
        return;
1006
1007
0
    SdrMarkList aRemove;
1008
0
    rMarkList.ForceSort();
1009
1010
0
    const bool bUndo = IsUndoEnabled();
1011
1012
0
    if( bUndo )
1013
0
        BegUndo();
1014
1015
0
    size_t nInsPos = SAL_MAX_SIZE;
1016
0
    const SdrObject* pAttrObj = nullptr;
1017
0
    basegfx::B2DPolyPolygon aMergePolyPolygonA;
1018
0
    basegfx::B2DPolyPolygon aMergePolyPolygonB;
1019
1020
0
    SdrObjList* pInsOL = nullptr;
1021
0
    SdrPageView* pInsPV = nullptr;
1022
0
    bool bFirstObjectComplete(false);
1023
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
0
    ConvertMarkedToPathObj(true);
1030
0
    OSL_ENSURE(rMarkList.GetMarkCount() != 0, "no more objects selected after preparations (!)");
1031
1032
0
    for(size_t a=0; a<rMarkList.GetMarkCount(); ++a)
1033
0
    {
1034
0
        SdrMark* pM = rMarkList.GetMark(a);
1035
0
        SdrObject* pObj = pM->GetMarkedSdrObj();
1036
1037
0
        if(ImpCanConvertForCombine(pObj))
1038
0
        {
1039
0
            if(!pAttrObj)
1040
0
                pAttrObj = pObj;
1041
1042
0
            nInsPos = pObj->GetOrdNum() + 1;
1043
0
            pInsPV = pM->GetPageView();
1044
0
            pInsOL = pObj->getParentSdrObjListFromSdrObject();
1045
1046
            // #i76891# use single iteration from SJ here which works on SdrObjects and takes
1047
            // groups into account by itself
1048
0
            SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
1049
1050
0
            while(aIter.IsMore())
1051
0
            {
1052
0
                SdrObject* pCandidate = aIter.Next();
1053
0
                SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>( pCandidate );
1054
0
                if(pPathObj)
1055
0
                {
1056
0
                    basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly());
1057
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
0
                    aTmpPoly = basegfx::utils::simplifyCurveSegments(aTmpPoly);
1062
1063
                    // for each part polygon as preparation, remove self-intersections
1064
                    // correct orientations and get rid of possible neutral polygons.
1065
0
                    aTmpPoly = basegfx::utils::prepareForPolygonOperation(aTmpPoly);
1066
1067
0
                    if(!bFirstObjectComplete)
1068
0
                    {
1069
                        // #i111987# Also need to collect ORed source shape when more than
1070
                        // a single polygon is involved
1071
0
                        if(aMergePolyPolygonA.count())
1072
0
                        {
1073
0
                            aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly);
1074
0
                        }
1075
0
                        else
1076
0
                        {
1077
0
                            aMergePolyPolygonA = std::move(aTmpPoly);
1078
0
                        }
1079
0
                    }
1080
0
                    else
1081
0
                    {
1082
0
                        if(aMergePolyPolygonB.count())
1083
0
                        {
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
0
                            aMergePolyPolygonB = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly);
1088
0
                        }
1089
0
                        else
1090
0
                        {
1091
0
                            aMergePolyPolygonB = std::move(aTmpPoly);
1092
0
                        }
1093
0
                    }
1094
0
                }
1095
0
            }
1096
1097
            // was there something added to the first polygon?
1098
0
            if(!bFirstObjectComplete && aMergePolyPolygonA.count())
1099
0
            {
1100
0
                bFirstObjectComplete = true;
1101
0
            }
1102
1103
            // move object to temporary delete list
1104
0
            aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView()));
1105
0
        }
1106
0
    }
1107
1108
0
    switch(eMode)
1109
0
    {
1110
0
        case SdrMergeMode::Merge:
1111
0
        {
1112
            // merge all contained parts (OR)
1113
0
            aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
1114
0
            break;
1115
0
        }
1116
0
        case SdrMergeMode::Subtract:
1117
0
        {
1118
            // Subtract B from A
1119
0
            aMergePolyPolygonA = basegfx::utils::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB);
1120
0
            break;
1121
0
        }
1122
0
        case SdrMergeMode::Intersect:
1123
0
        {
1124
            // AND B and A
1125
0
            aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
1126
0
            break;
1127
0
        }
1128
0
    }
1129
1130
    // #i73441# check insert list before taking actions
1131
0
    if(pInsOL)
1132
0
    {
1133
0
        rtl::Reference<SdrPathObj> pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), SdrObjKind::PathFill, std::move(aMergePolyPolygonA));
1134
0
        ImpCopyAttributes(pAttrObj, pPath.get());
1135
0
        pInsOL->InsertObject(pPath.get(), nInsPos);
1136
0
        if( bUndo )
1137
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath));
1138
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
0
        UnmarkAllObj(pInsPV);
1143
1144
0
        MarkObj(pPath.get(), pInsPV, false, true);
1145
0
    }
1146
1147
0
    aRemove.ForceSort();
1148
0
    switch(eMode)
1149
0
    {
1150
0
        case SdrMergeMode::Merge:
1151
0
        {
1152
0
            SetUndoComment(
1153
0
                SvxResId(STR_EditMergeMergePoly),
1154
0
                aRemove.GetMarkDescription());
1155
0
            break;
1156
0
        }
1157
0
        case SdrMergeMode::Subtract:
1158
0
        {
1159
0
            SetUndoComment(
1160
0
                SvxResId(STR_EditMergeSubstractPoly),
1161
0
                aRemove.GetMarkDescription());
1162
0
            break;
1163
0
        }
1164
0
        case SdrMergeMode::Intersect:
1165
0
        {
1166
0
            SetUndoComment(
1167
0
                SvxResId(STR_EditMergeIntersectPoly),
1168
0
                aRemove.GetMarkDescription());
1169
0
            break;
1170
0
        }
1171
0
    }
1172
0
    DeleteMarkedList(aRemove);
1173
1174
0
    if( bUndo )
1175
0
        EndUndo();
1176
0
}
1177
1178
void SdrEditView::EqualizeMarkedObjects(bool bWidth)
1179
0
{
1180
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1181
0
    size_t nMarked = rMarkList.GetMarkCount();
1182
1183
0
    if (nMarked < 2)
1184
0
        return;
1185
1186
0
    size_t nLastSelected = 0;
1187
0
    sal_Int64 nLastSelectedTime = rMarkList.GetMark(0)->getTimeStamp();
1188
0
    for (size_t a = 1; a < nMarked; ++a)
1189
0
    {
1190
0
        sal_Int64 nCandidateTime = rMarkList.GetMark(a)->getTimeStamp();
1191
0
        if (nCandidateTime > nLastSelectedTime)
1192
0
        {
1193
0
            nLastSelectedTime = nCandidateTime;
1194
0
            nLastSelected = a;
1195
0
        }
1196
0
    }
1197
1198
0
    SdrObject* pLastSelectedObj = rMarkList.GetMark(nLastSelected)->GetMarkedSdrObj();
1199
0
    Size aLastRectSize(pLastSelectedObj->GetLogicRect().GetSize());
1200
1201
0
    const bool bUndo = IsUndoEnabled();
1202
1203
0
    if (bUndo)
1204
0
        BegUndo();
1205
1206
0
    for (size_t a = 0; a < nMarked; ++a)
1207
0
    {
1208
0
        if (a == nLastSelected)
1209
0
            continue;
1210
0
        SdrMark* pM = rMarkList.GetMark(a);
1211
0
        SdrObject* pObj = pM->GetMarkedSdrObj();
1212
0
        tools::Rectangle aLogicRect(pObj->GetLogicRect());
1213
0
        Size aLogicRectSize(aLogicRect.GetSize());
1214
0
        if (bWidth)
1215
0
            aLogicRectSize.setWidth( aLastRectSize.Width() );
1216
0
        else
1217
0
            aLogicRectSize.setHeight( aLastRectSize.Height() );
1218
0
        aLogicRect.SetSize(aLogicRectSize);
1219
0
        if (bUndo)
1220
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
1221
0
        pObj->SetLogicRect(aLogicRect);
1222
0
    }
1223
1224
0
    SetUndoComment(
1225
0
        SvxResId(bWidth ? STR_EqualizeWidthMarkedObjects : STR_EqualizeHeightMarkedObjects),
1226
0
        rMarkList.GetMarkDescription());
1227
1228
0
    if (bUndo)
1229
0
        EndUndo();
1230
0
}
1231
1232
void SdrEditView::CombineMarkedTextObjects()
1233
0
{
1234
0
    SdrPageView* pPageView = GetSdrPageView();
1235
0
    if ( !pPageView || pPageView->IsLayerLocked( GetActiveLayer() ) )
1236
0
        return;
1237
1238
0
    bool bUndo = IsUndoEnabled();
1239
1240
    // Undo-String will be set later
1241
0
    if ( bUndo )
1242
0
        BegUndo();
1243
1244
0
    SdrOutliner& rDrawOutliner = getSdrModelFromSdrView().GetDrawOutliner();
1245
1246
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1247
0
    SdrObjListIter aIter( rMarkList, SdrIterMode::Flat);
1248
0
    while ( aIter.IsMore() )
1249
0
    {
1250
0
        SdrObject* pObj = aIter.Next();
1251
0
        SdrTextObj* pTextObj = DynCastSdrTextObj( pObj );
1252
0
        const OutlinerParaObject* pOPO = pTextObj ? pTextObj->GetOutlinerParaObject() : nullptr;
1253
0
        if ( pOPO && pTextObj->IsTextFrame()
1254
0
             &&  pTextObj->GetObjIdentifier() == SdrObjKind::Text   // not callouts (OBJ_CAPTION)
1255
0
             && !pTextObj->IsOutlText()                     // not impress presentation objects
1256
0
             &&  pTextObj->GetMergedItem(XATTR_FORMTXTSTYLE).GetValue() == XFormTextStyle::NONE // not Fontwork
1257
0
           )
1258
0
        {
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
0
            const sal_Int32 nPara = rDrawOutliner.GetParagraphCount();
1262
0
            const OUString sLastPara = nPara ? rDrawOutliner.GetText( rDrawOutliner.GetParagraph( nPara - 1 ) ) : u""_ustr;
1263
0
            sal_Int32 n = sLastPara.getLength();
1264
0
            while ( n && unicode::isWhiteSpace( sLastPara[--n] ) )
1265
0
                ;
1266
            //TODO: find way to use Locale to identify sentence final punctuation. Copied IsSentenceAtEnd() from autofmt.cxx
1267
0
            const bool bAppend = !n || ( sLastPara[n] != '.' && sLastPara[n] != '?' && sLastPara[n] != '!' );
1268
0
            rDrawOutliner.AddText( *pOPO, bAppend );
1269
0
        }
1270
0
        else
1271
0
        {
1272
            // Unmark non-textboxes, because all marked objects are deleted at the end. AdjustMarkHdl later.
1273
0
            MarkObj(pObj, pPageView, /*bUnmark=*/true, /*bImpNoSetMarkHdl=*/true);
1274
0
        }
1275
0
    }
1276
1277
0
    MarkListHasChanged();
1278
0
    AdjustMarkHdl();
1279
1280
0
    if ( rMarkList.GetMarkCount() > 1 )
1281
0
    {
1282
0
        rtl::Reference<SdrRectObj> pReplacement = new SdrRectObj( getSdrModelFromSdrView(), tools::Rectangle(), SdrObjKind::Text );
1283
0
        pReplacement->SetOutlinerParaObject( rDrawOutliner.CreateParaObject() );
1284
0
        pReplacement->SetSnapRect( GetMarkedObjRect() );
1285
1286
0
        const SdrInsertFlags nFlags = SdrInsertFlags::DONTMARK | SdrInsertFlags::SETDEFLAYER;
1287
0
        if ( InsertObjectAtView( pReplacement.get(), *pPageView, nFlags ) )
1288
0
            DeleteMarkedObj();
1289
0
    }
1290
1291
0
    if ( bUndo )
1292
0
        EndUndo();
1293
1294
0
    return;
1295
0
}
1296
1297
void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly)
1298
0
{
1299
    // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would
1300
    // create a 2nd Undo-action and Undo-Comment.
1301
1302
0
    bool bUndo = IsUndoEnabled();
1303
1304
    // Undo-String will be set later
1305
0
    if( bUndo )
1306
0
        BegUndo(u""_ustr, u""_ustr, bNoPolyPoly ? SdrRepeatFunc::CombineOnePoly : SdrRepeatFunc::CombinePolyPoly);
1307
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.
1311
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.
1316
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);
1323
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
0
    ConvertMarkedToPathObj(false /* bLineToArea */);
1333
1334
    // continue as before
1335
0
    basegfx::B2DPolyPolygon aPolyPolygon;
1336
0
    SdrObjList* pCurrentOL = nullptr;
1337
0
    SdrMarkList aRemoveBuffer;
1338
1339
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1340
0
    rMarkList.ForceSort();
1341
0
    size_t nInsPos = SAL_MAX_SIZE;
1342
0
    SdrObjList* pInsOL = nullptr;
1343
0
    SdrPageView* pInsPV = nullptr;
1344
0
    const SdrObject* pAttrObj = nullptr;
1345
1346
0
    for(size_t a = rMarkList.GetMarkCount(); a; )
1347
0
    {
1348
0
        --a;
1349
0
        SdrMark* pM = rMarkList.GetMark(a);
1350
0
        SdrObject* pObj = pM->GetMarkedSdrObj();
1351
0
        SdrObjList* pThisOL = pObj->getParentSdrObjListFromSdrObject();
1352
1353
0
        if(pCurrentOL != pThisOL)
1354
0
        {
1355
0
            pCurrentOL = pThisOL;
1356
0
        }
1357
1358
0
        if(ImpCanConvertForCombine(pObj))
1359
0
        {
1360
            // remember objects to be able to copy attributes
1361
0
            pAttrObj = pObj;
1362
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
0
            basegfx::B2DPolyPolygon aTmpPoly(basegfx::utils::simplifyCurveSegments(ImpGetPolyPolygon(pObj)));
1367
0
            aPolyPolygon.insert(0, aTmpPoly);
1368
1369
0
            if(!pInsOL)
1370
0
            {
1371
0
                nInsPos = pObj->GetOrdNum() + 1;
1372
0
                pInsPV = pM->GetPageView();
1373
0
                pInsOL = pObj->getParentSdrObjListFromSdrObject();
1374
0
            }
1375
1376
0
            aRemoveBuffer.InsertEntry(SdrMark(pObj, pM->GetPageView()));
1377
0
        }
1378
0
    }
1379
1380
0
    if(bNoPolyPoly)
1381
0
    {
1382
0
        basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon));
1383
0
        aPolyPolygon.clear();
1384
0
        aPolyPolygon.append(aCombinedPolygon);
1385
0
    }
1386
1387
0
    const sal_uInt32 nPolyCount(aPolyPolygon.count());
1388
1389
0
    if (nPolyCount && pAttrObj)
1390
0
    {
1391
0
        SdrObjKind eKind = SdrObjKind::PathFill;
1392
1393
0
        if(nPolyCount > 1)
1394
0
        {
1395
0
            aPolyPolygon.setClosed(true);
1396
0
        }
1397
0
        else
1398
0
        {
1399
            // check for Polyline
1400
0
            const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
1401
0
            const sal_uInt32 nPointCount(aPolygon.count());
1402
1403
0
            if(nPointCount <= 2)
1404
0
            {
1405
0
                eKind = SdrObjKind::PathLine;
1406
0
            }
1407
0
            else
1408
0
            {
1409
0
                if(!aPolygon.isClosed())
1410
0
                {
1411
0
                    const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0));
1412
0
                    const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1));
1413
0
                    const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength());
1414
0
                    const double fJoinTolerance(10.0);
1415
1416
0
                    if(fDistance < fJoinTolerance)
1417
0
                    {
1418
0
                        aPolyPolygon.setClosed(true);
1419
0
                    }
1420
0
                    else
1421
0
                    {
1422
0
                        eKind = SdrObjKind::PathLine;
1423
0
                    }
1424
0
                }
1425
0
            }
1426
0
        }
1427
1428
0
        rtl::Reference<SdrPathObj> pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), eKind, std::move(aPolyPolygon));
1429
1430
        // attributes of the lowest object
1431
0
        ImpCopyAttributes(pAttrObj, pPath.get());
1432
1433
        // If LineStyle of pAttrObj is drawing::LineStyle_NONE force to drawing::LineStyle_SOLID to make visible.
1434
0
        const drawing::LineStyle eLineStyle = pAttrObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
1435
0
        const drawing::FillStyle eFillStyle = pAttrObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
1436
1437
        // Take fill style/closed state of pAttrObj in account when deciding to change the line style
1438
0
        bool bIsClosedPathObj = false;
1439
0
        if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pAttrObj))
1440
0
            if (pPathObj->IsClosed())
1441
0
                bIsClosedPathObj = true;
1442
1443
0
        if(drawing::LineStyle_NONE == eLineStyle && (drawing::FillStyle_NONE == eFillStyle || !bIsClosedPathObj))
1444
0
        {
1445
0
            pPath->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID));
1446
0
        }
1447
1448
0
        pInsOL->InsertObject(pPath.get(),nInsPos);
1449
0
        if( bUndo )
1450
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath));
1451
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
0
        UnmarkAllObj(pInsPV);
1457
0
        MarkObj(pPath.get(), pInsPV, false, true);
1458
0
    }
1459
1460
    // build an UndoComment from the objects actually used
1461
0
    aRemoveBuffer.ForceSort(); // important for remove (see below)
1462
0
    if( bUndo )
1463
0
        SetUndoComment(SvxResId(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveBuffer.GetMarkDescription());
1464
1465
    // remove objects actually used from the list
1466
0
    DeleteMarkedList(aRemoveBuffer);
1467
0
    if( bUndo )
1468
0
        EndUndo();
1469
0
}
1470
1471
1472
// Dismantle
1473
1474
1475
bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, bool bMakeLines)
1476
0
{
1477
0
    bool bCan(false);
1478
0
    const sal_uInt32 nPolygonCount(rPpolyPolygon.count());
1479
1480
0
    if(nPolygonCount >= 2)
1481
0
    {
1482
        // #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon
1483
0
        bCan = true;
1484
0
    }
1485
0
    else if(bMakeLines && 1 == nPolygonCount)
1486
0
    {
1487
        // #i69172# ..or with at least 2 edges (curves or lines)
1488
0
        const basegfx::B2DPolygon& aPolygon(rPpolyPolygon.getB2DPolygon(0));
1489
0
        const sal_uInt32 nPointCount(aPolygon.count());
1490
1491
0
        if(nPointCount > 2)
1492
0
        {
1493
0
            bCan = true;
1494
0
        }
1495
0
    }
1496
1497
0
    return bCan;
1498
0
}
1499
1500
bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, bool bMakeLines)
1501
0
{
1502
0
    bool bOtherObjs(false);    // true=objects other than PathObj's existent
1503
0
    bool bMin1PolyPoly(false); // true=at least 1 tools::PolyPolygon with more than one Polygon existent
1504
0
    SdrObjList* pOL = pObj->GetSubList();
1505
1506
0
    if(pOL)
1507
0
    {
1508
        // group object -- check all members if they're PathObjs
1509
0
        SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
1510
1511
0
        while(aIter.IsMore() && !bOtherObjs)
1512
0
        {
1513
0
            const SdrObject* pObj1 = aIter.Next();
1514
0
            const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj1 );
1515
1516
0
            if(pPath)
1517
0
            {
1518
0
                if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines))
1519
0
                {
1520
0
                    bMin1PolyPoly = true;
1521
0
                }
1522
1523
0
                SdrObjTransformInfoRec aInfo;
1524
0
                pObj1->TakeObjInfo(aInfo);
1525
1526
0
                if(!aInfo.bCanConvToPath)
1527
0
                {
1528
                    // happens e. g. in the case of FontWork
1529
0
                    bOtherObjs = true;
1530
0
                }
1531
0
            }
1532
0
            else
1533
0
            {
1534
0
                bOtherObjs = true;
1535
0
            }
1536
0
        }
1537
0
    }
1538
0
    else
1539
0
    {
1540
0
        const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>(pObj);
1541
0
        const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>(pObj);
1542
1543
        // #i37011#
1544
0
        if(pPath)
1545
0
        {
1546
0
            if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines))
1547
0
            {
1548
0
                bMin1PolyPoly = true;
1549
0
            }
1550
1551
0
            SdrObjTransformInfoRec aInfo;
1552
0
            pObj->TakeObjInfo(aInfo);
1553
1554
            // new condition IsLine() to be able to break simple Lines
1555
0
            if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine())
1556
0
            {
1557
                // happens e. g. in the case of FontWork
1558
0
                bOtherObjs = true;
1559
0
            }
1560
0
        }
1561
0
        else if(pCustomShape)
1562
0
        {
1563
0
            if(bMakeLines)
1564
0
            {
1565
                // allow break command
1566
0
                bMin1PolyPoly = true;
1567
0
            }
1568
0
        }
1569
0
        else
1570
0
        {
1571
0
            bOtherObjs = true;
1572
0
        }
1573
0
    }
1574
0
    return bMin1PolyPoly && !bOtherObjs;
1575
0
}
1576
1577
void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, size_t& rPos, SdrPageView* pPV, bool bMakeLines)
1578
0
{
1579
0
    const SdrPathObj* pSrcPath = dynamic_cast<const SdrPathObj*>( pObj );
1580
0
    const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>( pObj );
1581
1582
0
    const bool bUndo = IsUndoEnabled();
1583
1584
0
    if(pSrcPath)
1585
0
    {
1586
        // #i74631# redesigned due to XpolyPolygon removal and explicit constructors
1587
0
        SdrObject* pLast = nullptr; // to be able to apply OutlinerParaObject
1588
0
        const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly());
1589
0
        const sal_uInt32 nPolyCount(rPolyPolygon.count());
1590
1591
0
        for(sal_uInt32 a(0); a < nPolyCount; a++)
1592
0
        {
1593
0
            const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a));
1594
0
            const sal_uInt32 nPointCount(rCandidate.count());
1595
1596
0
            if(!bMakeLines || nPointCount < 2)
1597
0
            {
1598
0
                rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
1599
0
                    pSrcPath->getSdrModelFromSdrObject(),
1600
0
                    pSrcPath->GetObjIdentifier(),
1601
0
                    basegfx::B2DPolyPolygon(rCandidate));
1602
0
                ImpCopyAttributes(pSrcPath, pPath.get());
1603
0
                pLast = pPath.get();
1604
0
                rOL.InsertObject(pPath.get(), rPos);
1605
0
                if( bUndo )
1606
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
1607
0
                MarkObj(pPath.get(), pPV, false, true);
1608
0
                rPos++;
1609
0
            }
1610
0
            else
1611
0
            {
1612
0
                const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
1613
1614
0
                for(sal_uInt32 b(0); b < nLoopCount; b++)
1615
0
                {
1616
0
                    SdrObjKind eKind(SdrObjKind::PolyLine);
1617
0
                    basegfx::B2DPolygon aNewPolygon;
1618
0
                    const sal_uInt32 nNextIndex((b + 1) % nPointCount);
1619
1620
0
                    aNewPolygon.append(rCandidate.getB2DPoint(b));
1621
1622
0
                    if(rCandidate.areControlPointsUsed())
1623
0
                    {
1624
0
                        aNewPolygon.appendBezierSegment(
1625
0
                            rCandidate.getNextControlPoint(b),
1626
0
                            rCandidate.getPrevControlPoint(nNextIndex),
1627
0
                            rCandidate.getB2DPoint(nNextIndex));
1628
0
                        eKind = SdrObjKind::PathLine;
1629
0
                    }
1630
0
                    else
1631
0
                    {
1632
0
                        aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex));
1633
0
                    }
1634
1635
0
                    rtl::Reference<SdrPathObj> pPath = new SdrPathObj(
1636
0
                        pSrcPath->getSdrModelFromSdrObject(),
1637
0
                        eKind,
1638
0
                        basegfx::B2DPolyPolygon(aNewPolygon));
1639
0
                    ImpCopyAttributes(pSrcPath, pPath.get());
1640
0
                    pLast = pPath.get();
1641
0
                    rOL.InsertObject(pPath.get(), rPos);
1642
0
                    if( bUndo )
1643
0
                        AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
1644
0
                    MarkObj(pPath.get(), pPV, false, true);
1645
0
                    rPos++;
1646
0
                }
1647
0
            }
1648
0
        }
1649
1650
0
        if(pLast && pSrcPath->GetOutlinerParaObject())
1651
0
        {
1652
0
            pLast->SetOutlinerParaObject(*pSrcPath->GetOutlinerParaObject());
1653
0
        }
1654
0
    }
1655
0
    else if(pCustomShape)
1656
0
    {
1657
0
        if(bMakeLines)
1658
0
        {
1659
            // break up custom shape
1660
0
            const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape();
1661
1662
0
            if(pReplacement)
1663
0
            {
1664
0
                rtl::Reference<SdrObject> pCandidate(pReplacement->CloneSdrObject(pReplacement->getSdrModelFromSdrObject()));
1665
0
                DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)");
1666
1667
0
                if(pCustomShape->GetMergedItem(SDRATTR_SHADOW).GetValue())
1668
0
                {
1669
0
                    if(dynamic_cast<const SdrObjGroup*>( pReplacement) !=  nullptr)
1670
0
                    {
1671
0
                        pCandidate->SetMergedItem(makeSdrShadowItem(true));
1672
0
                    }
1673
0
                }
1674
1675
0
                rOL.InsertObject(pCandidate.get(), rPos);
1676
0
                if( bUndo )
1677
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true));
1678
0
                MarkObj(pCandidate.get(), pPV, false, true);
1679
1680
0
                if(pCustomShape->HasText() && !pCustomShape->IsTextPath())
1681
0
                {
1682
                    // #i37011# also create a text object and add at rPos + 1
1683
0
                    rtl::Reference<SdrObject> pTextObj = SdrObjFactory::MakeNewObject(
1684
0
                        pCustomShape->getSdrModelFromSdrObject(),
1685
0
                        pCustomShape->GetObjInventor(),
1686
0
                        SdrObjKind::Text);
1687
1688
                    // Copy text content
1689
0
                    OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject();
1690
0
                    if(pParaObj)
1691
0
                    {
1692
0
                        pTextObj->NbcSetOutlinerParaObject(*pParaObj);
1693
0
                    }
1694
1695
                    // copy all attributes
1696
0
                    SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet());
1697
1698
                    // clear fill and line style
1699
0
                    aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
1700
0
                    aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
1701
1702
                    // get the text bounds and set at text object
1703
0
                    tools::Rectangle aTextBounds = pCustomShape->GetSnapRect();
1704
0
                    if(pCustomShape->GetTextBounds(aTextBounds))
1705
0
                    {
1706
0
                        pTextObj->SetSnapRect(aTextBounds);
1707
0
                    }
1708
1709
                    // if rotated, copy GeoStat, too.
1710
0
                    const GeoStat& rSourceGeo = pCustomShape->GetGeoStat();
1711
0
                    if(rSourceGeo.m_nRotationAngle)
1712
0
                    {
1713
0
                        pTextObj->NbcRotate(
1714
0
                            pCustomShape->GetSnapRect().Center(), rSourceGeo.m_nRotationAngle,
1715
0
                            rSourceGeo.mfSinRotationAngle, rSourceGeo.mfCosRotationAngle);
1716
0
                    }
1717
1718
                    // set modified ItemSet at text object
1719
0
                    pTextObj->SetMergedItemSet(aTargetItemSet);
1720
1721
                    // insert object
1722
0
                    rOL.InsertObject(pTextObj.get(), rPos + 1);
1723
0
                    if( bUndo )
1724
0
                        AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true));
1725
0
                    MarkObj(pTextObj.get(), pPV, false, true);
1726
0
                }
1727
0
            }
1728
0
        }
1729
0
    }
1730
0
}
1731
1732
void SdrEditView::DismantleMarkedObjects(bool bMakeLines)
1733
0
{
1734
    // temporary MarkList
1735
0
    SdrMarkList aRemoveBuffer;
1736
1737
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1738
0
    rMarkList.ForceSort();
1739
1740
0
    const bool bUndo = IsUndoEnabled();
1741
1742
0
    if( bUndo )
1743
0
    {
1744
        // comment is constructed later
1745
0
        BegUndo(u""_ustr, u""_ustr, bMakeLines ? SdrRepeatFunc::DismantleLines : SdrRepeatFunc::DismantlePolys);
1746
0
    }
1747
1748
0
    SdrObjList* pOL0=nullptr;
1749
0
    const bool bWasLocked = GetModel().isLocked();
1750
0
    GetModel().setLock(true);
1751
0
    for (size_t nm=rMarkList.GetMarkCount(); nm>0;) {
1752
0
        --nm;
1753
0
        SdrMark* pM=rMarkList.GetMark(nm);
1754
0
        SdrObject* pObj=pM->GetMarkedSdrObj();
1755
0
        SdrPageView* pPV=pM->GetPageView();
1756
0
        SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
1757
0
        if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // make sure OrdNums are correct!
1758
0
        if (ImpCanDismantle(pObj,bMakeLines)) {
1759
0
            assert(pOL);
1760
0
            aRemoveBuffer.InsertEntry(SdrMark(pObj,pM->GetPageView()));
1761
0
            const size_t nPos0=pObj->GetOrdNumDirect();
1762
0
            size_t nPos=nPos0+1;
1763
0
            SdrObjList* pSubList=pObj->GetSubList();
1764
0
            if (pSubList!=nullptr && !pObj->Is3DObj()) {
1765
0
                SdrObjListIter aIter(pSubList,SdrIterMode::DeepNoGroups);
1766
0
                while (aIter.IsMore()) {
1767
0
                    const SdrObject* pObj1=aIter.Next();
1768
0
                    ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines);
1769
0
                }
1770
0
            } else {
1771
0
                ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines);
1772
0
            }
1773
0
            if( bUndo )
1774
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,true));
1775
0
            pOL->RemoveObject(nPos0);
1776
0
        }
1777
0
    }
1778
0
    GetModel().setLock(bWasLocked);
1779
1780
0
    if( bUndo )
1781
0
    {
1782
        // construct UndoComment from objects actually used
1783
0
        SetUndoComment(SvxResId(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveBuffer.GetMarkDescription());
1784
        // remove objects actually used from the list
1785
0
        EndUndo();
1786
0
    }
1787
0
}
1788
1789
1790
// Group
1791
1792
1793
void SdrEditView::GroupMarked()
1794
2.94k
{
1795
2.94k
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1796
2.94k
    if (rMarkList.GetMarkCount() == 0)
1797
2.94k
        return;
1798
1799
0
    rMarkList.ForceSort();
1800
1801
0
    const bool bUndo = IsUndoEnabled();
1802
0
    if( bUndo )
1803
0
    {
1804
0
        BegUndo(SvxResId(STR_EditGroup),rMarkList.GetMarkDescription(),SdrRepeatFunc::Group);
1805
1806
0
        for(size_t nm = rMarkList.GetMarkCount(); nm>0; )
1807
0
        {
1808
            // add UndoActions for all affected objects
1809
0
            --nm;
1810
0
            SdrMark* pM=rMarkList.GetMark(nm);
1811
0
            SdrObject* pObj = pM->GetMarkedSdrObj();
1812
0
            AddUndoActions( CreateConnectorUndo( *pObj ) );
1813
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoRemoveObject( *pObj ));
1814
0
        }
1815
0
    }
1816
1817
0
    SdrMarkList aNewMark;
1818
0
    SdrPageView* pPV = GetSdrPageView();
1819
1820
0
    if(pPV)
1821
0
    {
1822
0
        SdrObjList* pCurrentLst=pPV->GetObjList();
1823
0
        SdrObjList* pSrcLst=pCurrentLst;
1824
0
        SdrObjList* pSrcLst0=pSrcLst;
1825
        // make sure OrdNums are correct
1826
0
        if (pSrcLst->IsObjOrdNumsDirty())
1827
0
            pSrcLst->RecalcObjOrdNums();
1828
0
        rtl::Reference<SdrObject> pGrp;
1829
0
        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
0
        size_t      nInsPos=pSrcLst->GetObjCount();
1833
0
        bool        bNeedInsPos=true;
1834
0
        for (size_t nm=rMarkList.GetMarkCount(); nm>0;)
1835
0
        {
1836
0
            --nm;
1837
0
            SdrMark* pM=rMarkList.GetMark(nm);
1838
0
            if (pM->GetPageView()==pPV)
1839
0
            {
1840
0
                SdrObject* pObj=pM->GetMarkedSdrObj();
1841
0
                if (!pGrp)
1842
0
                {
1843
0
                    pGrp = new SdrObjGroup(pObj->getSdrModelFromSdrObject());
1844
0
                    pDstLst=pGrp->GetSubList();
1845
0
                    assert(pDstLst && "Alleged group object doesn't return object list.");
1846
0
                }
1847
0
                pSrcLst=pObj->getParentSdrObjListFromSdrObject();
1848
0
                if (pSrcLst!=pSrcLst0)
1849
0
                {
1850
0
                    if (pSrcLst->IsObjOrdNumsDirty())
1851
0
                        pSrcLst->RecalcObjOrdNums();
1852
0
                }
1853
0
                bool bForeignList=pSrcLst!=pCurrentLst;
1854
0
                if (!bForeignList && bNeedInsPos)
1855
0
                {
1856
0
                    nInsPos=pObj->GetOrdNum(); // this way, all ObjOrdNum of the page are set
1857
0
                    nInsPos++;
1858
0
                    bNeedInsPos=false;
1859
0
                }
1860
0
                pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
1861
0
                if (!bForeignList)
1862
0
                    nInsPos--; // correct InsertPos
1863
0
                pDstLst->InsertObject(pObj,0);
1864
0
                GetMarkedObjectListWriteAccess().DeleteMark(nm);
1865
0
                pSrcLst0=pSrcLst;
1866
0
            }
1867
0
        }
1868
0
        if (pGrp!=nullptr)
1869
0
        {
1870
0
            assert(pDstLst); // keep coverity happy
1871
0
            aNewMark.InsertEntry(SdrMark(pGrp.get(),pPV));
1872
0
            const size_t nCount=pDstLst->GetObjCount();
1873
0
            pCurrentLst->InsertObject(pGrp.get(),nInsPos);
1874
0
            if( bUndo )
1875
0
            {
1876
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // no recalculation!
1877
0
                for (size_t no=0; no<nCount; ++no)
1878
0
                {
1879
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
1880
0
                }
1881
0
            }
1882
0
        }
1883
0
    }
1884
0
    GetMarkedObjectListWriteAccess().Merge(aNewMark);
1885
0
    MarkListHasChanged();
1886
1887
0
    if( bUndo )
1888
0
        EndUndo();
1889
0
}
1890
1891
1892
// Ungroup
1893
1894
1895
void SdrEditView::UnGroupMarked()
1896
0
{
1897
0
    SdrMarkList aNewMark;
1898
1899
0
    const bool bUndo = IsUndoEnabled();
1900
0
    if( bUndo )
1901
0
        BegUndo(u""_ustr, u""_ustr, SdrRepeatFunc::Ungroup);
1902
1903
0
    size_t nCount=0;
1904
0
    OUString aName1;
1905
0
    OUString aName;
1906
0
    bool bNameOk=false;
1907
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1908
0
    for (size_t nm=rMarkList.GetMarkCount(); nm>0;) {
1909
0
        --nm;
1910
0
        SdrMark* pM=rMarkList.GetMark(nm);
1911
0
        SdrObject* pGrp=pM->GetMarkedSdrObj();
1912
0
        SdrObjList* pSrcLst=pGrp->GetSubList();
1913
0
        if (pSrcLst!=nullptr) {
1914
0
            nCount++;
1915
0
            if (nCount==1) {
1916
0
                aName = pGrp->TakeObjNameSingul();  // retrieve name of group
1917
0
                aName1 = pGrp->TakeObjNamePlural(); // retrieve name of group
1918
0
                bNameOk=true;
1919
0
            } else {
1920
0
                if (nCount==2) aName=aName1; // set plural name
1921
0
                if (bNameOk) {
1922
0
                    OUString aStr(pGrp->TakeObjNamePlural()); // retrieve name of group
1923
1924
0
                    if (aStr != aName)
1925
0
                        bNameOk = false;
1926
0
                }
1927
0
            }
1928
0
            size_t nDstCnt=pGrp->GetOrdNum();
1929
0
            SdrObjList* pDstLst=pM->GetPageView()->GetObjList();
1930
0
            size_t nObjCount=pSrcLst->GetObjCount();
1931
0
            const bool bIsDiagram(pGrp->isDiagram());
1932
1933
            // If the Group is a Diagram, it has a filler BG object to guarantee
1934
            // the Diagam's dimensions. Identify that shape
1935
0
            if(bIsDiagram && nObjCount)
1936
0
            {
1937
0
                SdrObject* pObj(pSrcLst->GetObj(0));
1938
1939
0
                if(nullptr != pObj && !pObj->IsGroupObject() &&
1940
0
                    !pObj->HasLineStyle() &&
1941
0
                    pObj->IsMoveProtect() && pObj->IsResizeProtect())
1942
0
                {
1943
0
                    if(pObj->HasFillStyle())
1944
0
                    {
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.
1948
1949
                        // Undo of these is handled by SdrUndoGeoObj which holds a SdrObjGeoData,
1950
                        // create one
1951
0
                        if( bUndo )
1952
0
                            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
1953
1954
0
                        pObj->SetMoveProtect(false);
1955
0
                        pObj->SetResizeProtect(false);
1956
0
                    }
1957
0
                    else
1958
0
                    {
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
0
                        if( bUndo )
1962
0
                            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
1963
1964
0
                        pSrcLst->RemoveObject(0);
1965
1966
0
                        nObjCount = pSrcLst->GetObjCount();
1967
0
                    }
1968
0
                }
1969
0
            }
1970
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
0
            if( bUndo )
1975
0
            {
1976
0
                for (size_t no=nObjCount; no>0;)
1977
0
                {
1978
0
                    no--;
1979
0
                    SdrObject* pObj=pSrcLst->GetObj(no);
1980
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoRemoveObject(*pObj));
1981
0
                }
1982
0
            }
1983
1984
0
            for (size_t no=0; no<nObjCount; ++no)
1985
0
            {
1986
0
                rtl::Reference<SdrObject> pObj=pSrcLst->RemoveObject(0);
1987
0
                pDstLst->InsertObject(pObj.get(),nDstCnt);
1988
0
                if( bUndo )
1989
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true));
1990
0
                nDstCnt++;
1991
                // No SortCheck when inserting into MarkList, because that would
1992
                // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum():
1993
0
                aNewMark.InsertEntry(SdrMark(pObj.get(),pM->GetPageView()),false);
1994
0
            }
1995
1996
0
            if( bUndo )
1997
0
            {
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
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp));
2003
0
            }
2004
0
            pDstLst->RemoveObject(nDstCnt);
2005
2006
0
            GetMarkedObjectListWriteAccess().DeleteMark(nm);
2007
0
        }
2008
0
    }
2009
0
    if (nCount!=0)
2010
0
    {
2011
0
        if (!bNameOk)
2012
0
            aName=SvxResId(STR_ObjNamePluralGRUP); // Use the term "Group Objects," if different objects are grouped.
2013
0
        SetUndoComment(SvxResId(STR_EditUngroup),aName);
2014
0
    }
2015
2016
0
    if( bUndo )
2017
0
        EndUndo();
2018
2019
0
    if (nCount!=0)
2020
0
    {
2021
0
        GetMarkedObjectListWriteAccess().Merge(aNewMark,true); // Because of the sorting above, aNewMark is reversed
2022
0
        MarkListHasChanged();
2023
0
    }
2024
0
}
2025
2026
2027
// ConvertToPoly
2028
2029
2030
rtl::Reference<SdrObject> SdrEditView::ImpConvertOneObj(SdrObject* pObj, bool bPath, bool bLineToArea)
2031
0
{
2032
0
    rtl::Reference<SdrObject> pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea);
2033
0
    if (pNewObj)
2034
0
    {
2035
0
        SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
2036
0
        const bool bUndo = IsUndoEnabled();
2037
0
        if( bUndo )
2038
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj));
2039
2040
0
        pOL->ReplaceObject(pNewObj.get(), pObj->GetOrdNum());
2041
0
    }
2042
0
    return pNewObj;
2043
0
}
2044
2045
void SdrEditView::ImpConvertTo(bool bPath, bool bLineToArea)
2046
0
{
2047
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
2048
0
    if (rMarkList.GetMarkCount() == 0)
2049
0
        return;
2050
2051
0
    bool bMrkChg = false;
2052
0
    const size_t nMarkCount=rMarkList.GetMarkCount();
2053
0
    TranslateId pDscrID;
2054
0
    if(bLineToArea)
2055
0
    {
2056
0
        if(nMarkCount == 1)
2057
0
            pDscrID = STR_EditConvToContour;
2058
0
        else
2059
0
            pDscrID = STR_EditConvToContours;
2060
2061
0
        BegUndo(SvxResId(pDscrID), rMarkList.GetMarkDescription());
2062
0
    }
2063
0
    else
2064
0
    {
2065
0
        if (bPath) {
2066
0
            if (nMarkCount==1) pDscrID=STR_EditConvToCurve;
2067
0
            else pDscrID=STR_EditConvToCurves;
2068
0
            BegUndo(SvxResId(pDscrID),rMarkList.GetMarkDescription(),SdrRepeatFunc::ConvertToPath);
2069
0
        } else {
2070
0
            if (nMarkCount==1) pDscrID=STR_EditConvToPoly;
2071
0
            else pDscrID=STR_EditConvToPolys;
2072
0
            BegUndo(SvxResId(pDscrID),rMarkList.GetMarkDescription(),SdrRepeatFunc::ConvertToPoly);
2073
0
        }
2074
0
    }
2075
0
    for (size_t nm=nMarkCount; nm>0;) {
2076
0
        --nm;
2077
0
        SdrMark* pM=rMarkList.GetMark(nm);
2078
0
        SdrObject* pObj=pM->GetMarkedSdrObj();
2079
0
        SdrPageView* pPV=pM->GetPageView();
2080
0
        if (pObj->IsGroupObject() && !pObj->Is3DObj()) {
2081
0
            SdrObject* pGrp=pObj;
2082
0
            SdrObjListIter aIter(*pGrp, SdrIterMode::DeepNoGroups);
2083
0
            while (aIter.IsMore()) {
2084
0
                pObj=aIter.Next();
2085
0
                ImpConvertOneObj(pObj,bPath,bLineToArea);
2086
0
            }
2087
0
        } else {
2088
0
            rtl::Reference<SdrObject> pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
2089
0
            if (pNewObj) {
2090
0
                bMrkChg=true;
2091
0
                GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj.get(),pPV),nm);
2092
0
            }
2093
0
        }
2094
0
    }
2095
0
    EndUndo();
2096
0
    if (bMrkChg)
2097
0
    {
2098
0
        AdjustMarkHdl();
2099
0
        MarkListHasChanged();
2100
0
    }
2101
0
}
2102
2103
void SdrEditView::ConvertMarkedToPathObj(bool bLineToArea)
2104
0
{
2105
0
    ImpConvertTo(true, bLineToArea);
2106
0
}
2107
2108
void SdrEditView::ConvertMarkedToPolyObj()
2109
0
{
2110
0
    ImpConvertTo(false, false/*bLineToArea*/);
2111
0
}
2112
2113
namespace
2114
{
2115
    GDIMetaFile GetMetaFile(SdrGrafObj const * pGraf)
2116
0
    {
2117
0
        if (pGraf->HasGDIMetaFile())
2118
0
            return pGraf->GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
2119
0
        assert(pGraf->isEmbeddedVectorGraphicData());
2120
0
        return pGraf->getMetafileFromEmbeddedVectorGraphicData();
2121
0
    }
2122
}
2123
2124
// Metafile Import
2125
void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo)
2126
0
{
2127
0
    const bool bUndo = IsUndoEnabled();
2128
2129
0
    if( bUndo )
2130
0
        BegUndo(u""_ustr, u""_ustr, SdrRepeatFunc::ImportMtf);
2131
2132
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
2133
0
    rMarkList.ForceSort();
2134
0
    SdrMarkList aForTheDescription;
2135
0
    SdrMarkList aNewMarked;
2136
0
    for (size_t nm =rMarkList.GetMarkCount(); nm > 0; )
2137
0
    {
2138
        // create Undo objects for all new objects
2139
        // check for cancellation between the metafiles
2140
0
        if (pProgrInfo != nullptr)
2141
0
        {
2142
0
            pProgrInfo->SetNextObject();
2143
0
            if (!pProgrInfo->ReportActions(0))
2144
0
                break;
2145
0
        }
2146
2147
0
        --nm;
2148
0
        SdrMark*     pM=rMarkList.GetMark(nm);
2149
0
        SdrObject*   pObj=pM->GetMarkedSdrObj();
2150
0
        SdrPageView* pPV=pM->GetPageView();
2151
0
        SdrObjList*  pOL=pObj->getParentSdrObjListFromSdrObject();
2152
0
        const size_t nInsPos=pObj->GetOrdNum()+1;
2153
0
        size_t      nInsCnt=0;
2154
0
        tools::Rectangle aLogicRect;
2155
2156
0
        SdrGrafObj*  pGraf = dynamic_cast<SdrGrafObj*>( pObj );
2157
0
        if (pGraf != nullptr)
2158
0
        {
2159
0
            Graphic aGraphic = pGraf->GetGraphic();
2160
0
            auto const & pVectorGraphicData = aGraphic.getVectorGraphicData();
2161
2162
0
            if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Pdf)
2163
0
            {
2164
0
                auto pPdfium = vcl::pdf::PDFiumLibrary::get();
2165
0
                if (pPdfium)
2166
0
                {
2167
0
                    aLogicRect = pGraf->GetLogicRect();
2168
0
                    ImpSdrPdfImport aFilter(GetModel(), pObj->GetLayer(), aLogicRect, aGraphic);
2169
0
                    if (aGraphic.getPageNumber() < aFilter.GetPageCount())
2170
0
                    {
2171
0
                        nInsCnt = aFilter.DoImport(*pOL, nInsPos, aGraphic.getPageNumber(), pProgrInfo);
2172
0
                    }
2173
0
                }
2174
0
            }
2175
0
            else if (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedVectorGraphicData() )
2176
0
            {
2177
0
                GDIMetaFile aMetaFile(GetMetaFile(pGraf));
2178
0
                if (aMetaFile.GetActionSize())
2179
0
                {
2180
0
                    aLogicRect = pGraf->GetLogicRect();
2181
0
                    ImpSdrGDIMetaFileImport aFilter(GetModel(), pObj->GetLayer(), aLogicRect);
2182
0
                    nInsCnt = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo);
2183
0
                }
2184
0
            }
2185
0
        }
2186
2187
0
        SdrOle2Obj* pOle2 = dynamic_cast<SdrOle2Obj*>(pObj);
2188
0
        if (pOle2)
2189
0
        {
2190
0
            if (const Graphic* pGraphic = pOle2->GetGraphic())
2191
0
            {
2192
0
                aLogicRect = pOle2->GetLogicRect();
2193
0
                ImpSdrGDIMetaFileImport aFilter(GetModel(), pObj->GetLayer(), aLogicRect);
2194
0
                nInsCnt = aFilter.DoImport(pGraphic->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo);
2195
0
            }
2196
0
        }
2197
2198
0
        if (nInsCnt != 0)
2199
0
        {
2200
            // transformation
2201
0
            GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat());
2202
0
            size_t nObj = nInsPos;
2203
2204
0
            if (aGeoStat.m_nShearAngle)
2205
0
                aGeoStat.RecalcTan();
2206
2207
0
            if (aGeoStat.m_nRotationAngle)
2208
0
                aGeoStat.RecalcSinCos();
2209
2210
0
            for (size_t i = 0; i < nInsCnt; i++)
2211
0
            {
2212
0
                if (bUndo)
2213
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));
2214
2215
                // update new MarkList
2216
0
                SdrObject* pCandidate = pOL->GetObj(nObj);
2217
2218
                // apply original transformation
2219
0
                if (aGeoStat.m_nShearAngle)
2220
0
                    pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
2221
2222
0
                if (aGeoStat.m_nRotationAngle)
2223
0
                    pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
2224
2225
0
                SdrMark aNewMark(pCandidate, pPV);
2226
0
                aNewMarked.InsertEntry(aNewMark);
2227
2228
0
                nObj++;
2229
0
            }
2230
2231
0
            aForTheDescription.InsertEntry(*pM);
2232
2233
0
            if (bUndo)
2234
0
                AddUndo(GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
2235
2236
            // remove object from selection and delete
2237
0
            GetMarkedObjectListWriteAccess().DeleteMark(rMarkList.FindObject(pObj));
2238
0
            pOL->RemoveObject(nInsPos-1);
2239
0
        }
2240
0
    }
2241
2242
0
    if (aNewMarked.GetMarkCount())
2243
0
    {
2244
        // create new selection
2245
0
        for (size_t a = 0; a < aNewMarked.GetMarkCount(); ++a)
2246
0
        {
2247
0
            GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
2248
0
        }
2249
2250
0
        rMarkList.ForceSort();
2251
0
    }
2252
2253
0
    if (bUndo)
2254
0
    {
2255
0
        SetUndoComment(SvxResId(STR_EditImportMtf),aForTheDescription.GetMarkDescription());
2256
0
        EndUndo();
2257
0
    }
2258
0
}
2259
2260
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */