Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/svx/source/svdraw/svdocirc.cxx
Line
Count
Source (jump to first uncovered line)
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 <basegfx/matrix/b2dhommatrix.hxx>
21
#include <basegfx/matrix/b2dhommatrixtools.hxx>
22
#include <basegfx/point/b2dpoint.hxx>
23
#include <basegfx/polygon/b2dpolygon.hxx>
24
#include <basegfx/polygon/b2dpolygontools.hxx>
25
#include <math.h>
26
#include <rtl/ustrbuf.hxx>
27
28
#include <svx/dialmgr.hxx>
29
#include <svx/strings.hrc>
30
31
#include <sdr/contact/viewcontactofsdrcircobj.hxx>
32
#include <sdr/properties/circleproperties.hxx>
33
#include <svx/svddrag.hxx>
34
#include <svx/svdmodel.hxx>
35
#include <svx/svdocirc.hxx>
36
#include <svx/svdopath.hxx>
37
#include <svx/svdtrans.hxx>
38
#include <svx/svdview.hxx>
39
#include <svx/sxciaitm.hxx>
40
#include <sxcikitm.hxx>
41
#include <svx/xfillit0.hxx>
42
#include <svx/xlineit0.hxx>
43
#include <svx/xlnedit.hxx>
44
#include <svx/xlnedwit.hxx>
45
#include <svx/xlnstit.hxx>
46
#include <svx/xlnstwit.hxx>
47
#include <svx/xlnwtit.hxx>
48
#include <vcl/canvastools.hxx>
49
#include <vcl/ptrstyle.hxx>
50
51
using namespace com::sun::star;
52
53
static Point GetAnglePnt(const tools::Rectangle& rR, Degree100 nAngle)
54
0
{
55
0
    Point aCenter(rR.Center());
56
0
    tools::Long nWdt=rR.Right()-rR.Left();
57
0
    tools::Long nHgt=rR.Bottom()-rR.Top();
58
0
    tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
59
0
    double a = toRadians(nAngle);
60
0
    Point aRetval(basegfx::fround<tools::Long>(cos(a) * nMaxRad),
61
0
                  basegfx::fround<tools::Long>(-sin(a) * nMaxRad));
62
0
    if (nWdt==0) aRetval.setX(0 );
63
0
    if (nHgt==0) aRetval.setY(0 );
64
0
    if (nWdt!=nHgt) {
65
0
        if (nWdt>nHgt) {
66
0
            if (nWdt!=0) {
67
                // stop possible overruns for very large objects
68
0
                if (std::abs(nHgt)>32767 || std::abs(aRetval.Y())>32767) {
69
0
                    aRetval.setY(BigMulDiv(aRetval.Y(),nHgt,nWdt) );
70
0
                } else {
71
0
                    aRetval.setY(aRetval.Y()*nHgt/nWdt );
72
0
                }
73
0
            }
74
0
        } else {
75
0
            if (nHgt!=0) {
76
                // stop possible overruns for very large objects
77
0
                if (std::abs(nWdt)>32767 || std::abs(aRetval.X())>32767) {
78
0
                    aRetval.setX(BigMulDiv(aRetval.X(),nWdt,nHgt) );
79
0
                } else {
80
0
                    aRetval.setX(aRetval.X()*nWdt/nHgt );
81
0
                }
82
0
            }
83
0
        }
84
0
    }
85
0
    aRetval+=aCenter;
86
0
    return aRetval;
87
0
}
88
89
90
// BaseProperties section
91
92
std::unique_ptr<sdr::properties::BaseProperties> SdrCircObj::CreateObjectSpecificProperties()
93
17.6k
{
94
17.6k
    return std::make_unique<sdr::properties::CircleProperties>(*this);
95
17.6k
}
96
97
98
// DrawContact section
99
100
std::unique_ptr<sdr::contact::ViewContact> SdrCircObj::CreateObjectSpecificViewContact()
101
17.7k
{
102
17.7k
    return std::make_unique<sdr::contact::ViewContactOfSdrCircObj>(*this);
103
17.7k
}
104
105
SdrCircKind ToSdrCircKind(SdrObjKind eKind)
106
214
{
107
214
    switch (eKind)
108
214
    {
109
214
        case SdrObjKind::CircleOrEllipse: return SdrCircKind::Full;
110
0
        case SdrObjKind::CircleSection: return SdrCircKind::Section;
111
0
        case SdrObjKind::CircleArc: return SdrCircKind::Arc;
112
0
        case SdrObjKind::CircleCut: return SdrCircKind::Cut;
113
0
        default: assert(false);
114
214
    }
115
0
    return SdrCircKind::Full;
116
214
}
117
118
SdrCircObj::SdrCircObj(
119
    SdrModel& rSdrModel,
120
    SdrCircKind eNewKind)
121
0
:   SdrRectObj(rSdrModel)
122
0
{
123
0
    m_nStartAngle=0_deg100;
124
0
    m_nEndAngle=36000_deg100;
125
0
    meCircleKind=eNewKind;
126
0
    m_bClosedObj=eNewKind!=SdrCircKind::Arc;
127
0
}
128
129
SdrCircObj::SdrCircObj(SdrModel& rSdrModel, SdrCircObj const & rSource)
130
102
:   SdrRectObj(rSdrModel, rSource)
131
102
{
132
102
    meCircleKind = rSource.meCircleKind;
133
102
    m_nStartAngle = rSource.m_nStartAngle;
134
102
    m_nEndAngle = rSource.m_nEndAngle;
135
102
    m_bClosedObj = rSource.m_bClosedObj;
136
102
}
137
138
SdrCircObj::SdrCircObj(
139
    SdrModel& rSdrModel,
140
    SdrCircKind eNewKind,
141
    const tools::Rectangle& rRect)
142
1.69k
:   SdrRectObj(rSdrModel, rRect)
143
1.69k
{
144
1.69k
    m_nStartAngle=0_deg100;
145
1.69k
    m_nEndAngle=36000_deg100;
146
1.69k
    meCircleKind=eNewKind;
147
1.69k
    m_bClosedObj=eNewKind!=SdrCircKind::Arc;
148
1.69k
}
149
150
SdrCircObj::SdrCircObj(
151
    SdrModel& rSdrModel,
152
    SdrCircKind eNewKind,
153
    const tools::Rectangle& rRect,
154
    Degree100 nNewStartAngle,
155
    Degree100 nNewEndAngle)
156
15.9k
:   SdrRectObj(rSdrModel, rRect)
157
15.9k
{
158
15.9k
    Degree100 nAngleDif=nNewEndAngle-nNewStartAngle;
159
15.9k
    m_nStartAngle=NormAngle36000(nNewStartAngle);
160
15.9k
    m_nEndAngle=NormAngle36000(nNewEndAngle);
161
15.9k
    if (nAngleDif==36000_deg100) m_nEndAngle+=nAngleDif; // full circle
162
15.9k
    meCircleKind=eNewKind;
163
15.9k
    m_bClosedObj=eNewKind!=SdrCircKind::Arc;
164
15.9k
}
165
166
SdrCircObj::~SdrCircObj()
167
17.7k
{
168
17.7k
}
169
170
void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
171
0
{
172
0
    bool bCanConv=!HasText() || ImpCanConvTextToCurve();
173
0
    rInfo.bEdgeRadiusAllowed    = false;
174
0
    rInfo.bCanConvToPath=bCanConv;
175
0
    rInfo.bCanConvToPoly=bCanConv;
176
0
    rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
177
0
}
178
179
SdrObjKind SdrCircObj::GetObjIdentifier() const
180
25.8k
{
181
25.8k
    switch (meCircleKind)
182
25.8k
    {
183
9.62k
    case SdrCircKind::Full: return SdrObjKind::CircleOrEllipse;
184
8.94k
    case SdrCircKind::Section: return SdrObjKind::CircleSection;
185
0
    case SdrCircKind::Cut: return SdrObjKind::CircleCut;
186
7.32k
    case SdrCircKind::Arc: return SdrObjKind::CircleArc;
187
0
    default: assert(false);
188
25.8k
    }
189
0
    return SdrObjKind::CircleOrEllipse;
190
25.8k
}
191
192
bool SdrCircObj::PaintNeedsXPolyCirc() const
193
1.72k
{
194
    // XPoly is necessary for all rotated ellipse objects, circle and
195
    // ellipse segments.
196
    // If not WIN, then (for now) also for circle/ellipse segments and circle/
197
    // ellipse arcs (for precision)
198
1.72k
    bool bNeed = maGeo.m_nRotationAngle || maGeo.m_nShearAngle || meCircleKind == SdrCircKind::Cut;
199
    // If not WIN, then for everything except full circle (for now!)
200
1.72k
    if (meCircleKind!=SdrCircKind::Full) bNeed = true;
201
202
1.72k
    const SfxItemSet& rSet = GetObjectItemSet();
203
1.72k
    if(!bNeed)
204
1.50k
    {
205
        // XPoly is necessary for everything that isn't LineSolid or LineNone
206
1.50k
        drawing::LineStyle eLine = rSet.Get(XATTR_LINESTYLE).GetValue();
207
1.50k
        bNeed = eLine != drawing::LineStyle_NONE && eLine != drawing::LineStyle_SOLID;
208
209
        // XPoly is necessary for thick lines
210
1.50k
        if(!bNeed && eLine != drawing::LineStyle_NONE)
211
1.48k
            bNeed = rSet.Get(XATTR_LINEWIDTH).GetValue() != 0;
212
213
        // XPoly is necessary for circle arcs with line ends
214
1.50k
        if(!bNeed && meCircleKind == SdrCircKind::Arc)
215
0
        {
216
            // start of the line is here if StartPolygon, StartWidth!=0
217
0
            bNeed=rSet.Get(XATTR_LINESTART).GetLineStartValue().count() != 0 &&
218
0
                  rSet.Get(XATTR_LINESTARTWIDTH).GetValue() != 0;
219
220
0
            if(!bNeed)
221
0
            {
222
                // end of the line is here if EndPolygon, EndWidth!=0
223
0
                bNeed = rSet.Get(XATTR_LINEEND).GetLineEndValue().count() != 0 &&
224
0
                        rSet.Get(XATTR_LINEENDWIDTH).GetValue() != 0;
225
0
            }
226
0
        }
227
1.50k
    }
228
229
    // XPoly is necessary if Fill !=None and !=Solid
230
1.72k
    if(!bNeed && meCircleKind != SdrCircKind::Arc)
231
473
    {
232
473
        drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
233
473
        bNeed = eFill != drawing::FillStyle_NONE && eFill != drawing::FillStyle_SOLID;
234
473
    }
235
236
1.72k
    if(!bNeed && meCircleKind != SdrCircKind::Full && m_nStartAngle == m_nEndAngle)
237
0
        bNeed = true; // otherwise we're drawing a full circle
238
239
1.72k
    return bNeed;
240
1.72k
}
241
242
basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind, const tools::Rectangle& rRect1, Degree100 nStart, Degree100 nEnd) const
243
1.25k
{
244
1.25k
    const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRect1);
245
1.25k
    basegfx::B2DPolygon aCircPolygon;
246
247
1.25k
    if(SdrCircKind::Full == eCircleKind)
248
1.03k
    {
249
        // create full circle. Do not use createPolygonFromEllipse; it's necessary
250
        // to get the start point to the bottom of the circle to keep compatible to
251
        // old geometry creation
252
1.03k
        aCircPolygon = basegfx::utils::createPolygonFromUnitCircle(1);
253
254
        // needs own scaling and translation from unit circle to target size (same as
255
        // would be in createPolygonFromEllipse)
256
1.03k
        const basegfx::B2DPoint aCenter(aRange.getCenter());
257
1.03k
        const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
258
1.03k
            aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
259
1.03k
            aCenter.getX(), aCenter.getY()));
260
1.03k
        aCircPolygon.transform(aMatrix);
261
1.03k
    }
262
221
    else
263
221
    {
264
        // mirror start, end for geometry creation since model coordinate system is mirrored in Y
265
221
        const double fStart(toRadians((36000_deg100 - nEnd) % 36000_deg100));
266
221
        const double fEnd(toRadians((36000_deg100 - nStart) % 36000_deg100));
267
268
        // create circle segment. This is not closed by default
269
221
        aCircPolygon = basegfx::utils::createPolygonFromEllipseSegment(
270
221
            aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
271
221
            fStart, fEnd);
272
273
        // check closing states
274
221
        const bool bCloseSegment(SdrCircKind::Arc != eCircleKind);
275
221
        const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind);
276
277
221
        if(bCloseSegment)
278
221
        {
279
221
            if(bCloseUsingCenter)
280
221
            {
281
                // add center point at start (for historical reasons)
282
221
                basegfx::B2DPolygon aSector;
283
221
                aSector.append(aRange.getCenter());
284
221
                aSector.append(aCircPolygon);
285
221
                aCircPolygon = std::move(aSector);
286
221
            }
287
288
            // close
289
221
            aCircPolygon.setClosed(true);
290
221
        }
291
221
    }
292
293
    // #i76950#
294
1.25k
    if (maGeo.m_nShearAngle || maGeo.m_nRotationAngle)
295
0
    {
296
        // translate top left to (0,0)
297
0
        const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
298
0
        basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
299
0
            -aTopLeft.getX(), -aTopLeft.getY()));
300
301
        // shear, rotate and back to top left (if needed)
302
0
        aMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
303
0
            -maGeo.mfTanShearAngle,
304
0
            maGeo.m_nRotationAngle ? toRadians(36000_deg100 - maGeo.m_nRotationAngle) : 0.0,
305
0
            aTopLeft) * aMatrix;
306
307
        // apply transformation
308
0
        aCircPolygon.transform(aMatrix);
309
0
    }
310
311
1.25k
    return aCircPolygon;
312
1.25k
}
313
314
void SdrCircObj::RecalcXPoly()
315
1.25k
{
316
1.25k
    basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, getRectangle(), m_nStartAngle, m_nEndAngle));
317
1.25k
    mpXPoly = XPolygon(aPolyCirc);
318
1.25k
}
319
320
OUString SdrCircObj::TakeObjNameSingul() const
321
0
{
322
0
    TranslateId pID=STR_ObjNameSingulCIRC;
323
0
    if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.m_nShearAngle == 0_deg100)
324
0
    {
325
0
        switch (meCircleKind) {
326
0
            case SdrCircKind::Full: pID=STR_ObjNameSingulCIRC; break;
327
0
            case SdrCircKind::Section: pID=STR_ObjNameSingulSECT; break;
328
0
            case SdrCircKind::Arc: pID=STR_ObjNameSingulCARC; break;
329
0
            case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUT; break;
330
0
            default: break;
331
0
        }
332
0
    } else {
333
0
        switch (meCircleKind) {
334
0
            case SdrCircKind::Full: pID=STR_ObjNameSingulCIRCE; break;
335
0
            case SdrCircKind::Section: pID=STR_ObjNameSingulSECTE; break;
336
0
            case SdrCircKind::Arc: pID=STR_ObjNameSingulCARCE; break;
337
0
            case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUTE; break;
338
0
            default: break;
339
0
        }
340
0
    }
341
0
    OUString sName(SvxResId(pID));
342
343
0
    OUString aName(GetName());
344
0
    if (!aName.isEmpty())
345
0
        sName += " '" + aName + "'";
346
0
    return sName;
347
0
}
348
349
OUString SdrCircObj::TakeObjNamePlural() const
350
0
{
351
0
    TranslateId pID=STR_ObjNamePluralCIRC;
352
0
    if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.m_nShearAngle == 0_deg100)
353
0
    {
354
0
        switch (meCircleKind) {
355
0
            case SdrCircKind::Full: pID=STR_ObjNamePluralCIRC; break;
356
0
            case SdrCircKind::Section: pID=STR_ObjNamePluralSECT; break;
357
0
            case SdrCircKind::Arc: pID=STR_ObjNamePluralCARC; break;
358
0
            case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUT; break;
359
0
            default: break;
360
0
        }
361
0
    } else {
362
0
        switch (meCircleKind) {
363
0
            case SdrCircKind::Full: pID=STR_ObjNamePluralCIRCE; break;
364
0
            case SdrCircKind::Section: pID=STR_ObjNamePluralSECTE; break;
365
0
            case SdrCircKind::Arc: pID=STR_ObjNamePluralCARCE; break;
366
0
            case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUTE; break;
367
0
            default: break;
368
0
        }
369
0
    }
370
0
    return SvxResId(pID);
371
0
}
372
373
rtl::Reference<SdrObject> SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const
374
102
{
375
102
    return new SdrCircObj(rTargetModel, *this);
376
102
}
377
378
basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
379
0
{
380
0
    const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), m_nStartAngle, m_nEndAngle));
381
0
    return basegfx::B2DPolyPolygon(aCircPolygon);
382
0
}
383
384
namespace {
385
386
struct ImpCircUser : public SdrDragStatUserData
387
{
388
    tools::Rectangle                   aR;
389
    Point                       aCenter;
390
    Point                       aP1;
391
    tools::Long                        nHgt;
392
    tools::Long                        nWdt;
393
    Degree100                          nStart;
394
    Degree100                          nEnd;
395
396
public:
397
    ImpCircUser()
398
0
    :   nHgt(0),
399
0
        nWdt(0),
400
0
        nStart(0),
401
0
        nEnd(0)
402
0
    {}
403
    void SetCreateParams(SdrDragStat const & rStat);
404
};
405
406
}
407
408
sal_uInt32 SdrCircObj::GetHdlCount() const
409
0
{
410
0
    if(SdrCircKind::Full != meCircleKind)
411
0
    {
412
0
        return 10;
413
0
    }
414
0
    else
415
0
    {
416
0
        return 8;
417
0
    }
418
0
}
419
420
void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const
421
0
{
422
0
    for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum)
423
0
    {
424
0
        Point aPnt;
425
0
        SdrHdlKind eLocalKind(SdrHdlKind::Move);
426
0
        sal_uInt32 nPNum(0);
427
0
        tools::Rectangle aRectangle = getRectangle();
428
0
        switch (nHdlNum)
429
0
        {
430
0
            case 0:
431
0
                aPnt = GetAnglePnt(aRectangle, m_nStartAngle);
432
0
                eLocalKind = SdrHdlKind::Circle;
433
0
                nPNum = 1;
434
0
                break;
435
0
            case 1:
436
0
                aPnt = GetAnglePnt(aRectangle, m_nEndAngle);
437
0
                eLocalKind = SdrHdlKind::Circle;
438
0
                nPNum = 2;
439
0
                break;
440
0
            case 2:
441
0
                aPnt = aRectangle.TopLeft();
442
0
                eLocalKind = SdrHdlKind::UpperLeft;
443
0
                break;
444
0
            case 3:
445
0
                aPnt = aRectangle.TopCenter();
446
0
                eLocalKind = SdrHdlKind::Upper;
447
0
                break;
448
0
            case 4:
449
0
                aPnt = aRectangle.TopRight();
450
0
                eLocalKind = SdrHdlKind::UpperRight;
451
0
                break;
452
0
            case 5:
453
0
                aPnt = aRectangle.LeftCenter();
454
0
                eLocalKind = SdrHdlKind::Left;
455
0
                break;
456
0
            case 6:
457
0
                aPnt = aRectangle.RightCenter();
458
0
                eLocalKind = SdrHdlKind::Right;
459
0
                break;
460
0
            case 7:
461
0
                aPnt = aRectangle.BottomLeft();
462
0
                eLocalKind = SdrHdlKind::LowerLeft;
463
0
                break;
464
0
            case 8:
465
0
                aPnt = aRectangle.BottomCenter();
466
0
                eLocalKind = SdrHdlKind::Lower;
467
0
                break;
468
0
            case 9:
469
0
                aPnt = aRectangle.BottomRight();
470
0
                eLocalKind = SdrHdlKind::LowerRight;
471
0
                break;
472
0
        }
473
474
0
        if (maGeo.m_nShearAngle)
475
0
        {
476
0
            ShearPoint(aPnt, aRectangle.TopLeft(), maGeo.mfTanShearAngle);
477
0
        }
478
479
0
        if (maGeo.m_nRotationAngle)
480
0
        {
481
0
            RotatePoint(aPnt, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
482
0
        }
483
484
0
        std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eLocalKind));
485
0
        pH->SetPointNum(nPNum);
486
0
        pH->SetObj(const_cast<SdrCircObj*>(this));
487
0
        pH->SetRotationAngle(maGeo.m_nRotationAngle);
488
0
        rHdlList.AddHdl(std::move(pH));
489
0
    }
490
0
}
491
492
493
bool SdrCircObj::hasSpecialDrag() const
494
0
{
495
0
    return true;
496
0
}
497
498
bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
499
0
{
500
0
    const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
501
502
0
    if(bAngle)
503
0
    {
504
0
        if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
505
0
        {
506
0
            rDrag.SetNoSnap();
507
0
        }
508
509
0
        return true;
510
0
    }
511
512
0
    return SdrTextObj::beginSpecialDrag(rDrag);
513
0
}
514
515
bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
516
0
{
517
0
    const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
518
519
0
    if(bAngle)
520
0
    {
521
0
        Point aPt(rDrag.GetNow());
522
523
0
        if (maGeo.m_nRotationAngle)
524
0
            RotatePoint(aPt, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
525
526
0
        if (maGeo.m_nShearAngle)
527
0
            ShearPoint(aPt, getRectangle().TopLeft(), -maGeo.mfTanShearAngle);
528
529
0
        aPt -= getRectangle().Center();
530
531
0
        tools::Long nWdt = getRectangle().Right() - getRectangle().Left();
532
0
        tools::Long nHgt = getRectangle().Bottom() - getRectangle().Top();
533
534
0
        if(nWdt>=nHgt)
535
0
        {
536
0
            aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) );
537
0
        }
538
0
        else
539
0
        {
540
0
            aPt.setX(BigMulDiv(aPt.X(),nHgt,nWdt) );
541
0
        }
542
543
0
        Degree100 nAngle=NormAngle36000(GetAngle(aPt));
544
545
0
        if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
546
0
        {
547
0
            Degree100 nSA=rDrag.GetView()->GetSnapAngle();
548
549
0
            if (nSA)
550
0
            {
551
0
                nAngle+=nSA/2_deg100;
552
0
                nAngle/=nSA;
553
0
                nAngle*=nSA;
554
0
                nAngle=NormAngle36000(nAngle);
555
0
            }
556
0
        }
557
558
0
        if(1 == rDrag.GetHdl()->GetPointNum())
559
0
        {
560
0
            m_nStartAngle = nAngle;
561
0
        }
562
0
        else if(2 == rDrag.GetHdl()->GetPointNum())
563
0
        {
564
0
            m_nEndAngle = nAngle;
565
0
        }
566
567
0
        SetBoundAndSnapRectsDirty();
568
0
        SetXPolyDirty();
569
0
        ImpSetCircInfoToAttr();
570
0
        SetChanged();
571
572
0
        return true;
573
0
    }
574
0
    else
575
0
    {
576
0
        return SdrTextObj::applySpecialDrag(rDrag);
577
0
    }
578
0
}
579
580
OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
581
0
{
582
0
    const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
583
584
0
    if(bCreateComment)
585
0
    {
586
0
        OUStringBuffer aBuf(ImpGetDescriptionStr(STR_ViewCreateObj));
587
0
        const sal_uInt32 nPointCount(rDrag.GetPointCount());
588
589
0
        if(SdrCircKind::Full != meCircleKind && nPointCount > 2)
590
0
        {
591
0
            const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
592
0
            Degree100 nAngle;
593
594
0
            aBuf.append(" (");
595
596
0
            if(3 == nPointCount)
597
0
            {
598
0
                nAngle = pU->nStart;
599
0
            }
600
0
            else
601
0
            {
602
0
                nAngle = pU->nEnd;
603
0
            }
604
605
0
            aBuf.append(SdrModel::GetAngleString(nAngle));
606
0
            aBuf.append(')');
607
0
        }
608
609
0
        return aBuf.makeStringAndClear();
610
0
    }
611
0
    else
612
0
    {
613
0
        const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
614
615
0
        if(bAngle)
616
0
        {
617
0
            const Degree100 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? m_nStartAngle : m_nEndAngle);
618
619
0
            return ImpGetDescriptionStr(STR_DragCircAngle) +
620
0
                " (" +
621
0
                SdrModel::GetAngleString(nAngle) +
622
0
                ")";
623
0
        }
624
0
        else
625
0
        {
626
0
            return SdrTextObj::getSpecialDragComment(rDrag);
627
0
        }
628
0
    }
629
0
}
630
631
632
void ImpCircUser::SetCreateParams(SdrDragStat const & rStat)
633
0
{
634
0
    rStat.TakeCreateRect(aR);
635
0
    aR.Normalize();
636
0
    aCenter=aR.Center();
637
0
    nWdt=aR.Right()-aR.Left();
638
0
    nHgt=aR.Bottom()-aR.Top();
639
0
    nStart=0_deg100;
640
0
    nEnd=36000_deg100;
641
0
    if (rStat.GetPointCount()>2) {
642
0
        Point aP(rStat.GetPoint(2)-aCenter);
643
0
        if (nWdt==0) aP.setX(0 );
644
0
        if (nHgt==0) aP.setY(0 );
645
0
        if (nWdt>=nHgt) {
646
0
            if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt );
647
0
        } else {
648
0
            if (nWdt!=0) aP.setX(aP.X()*nHgt/nWdt );
649
0
        }
650
0
        nStart=NormAngle36000(GetAngle(aP));
651
0
        if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
652
0
            Degree100 nSA=rStat.GetView()->GetSnapAngle();
653
0
            if (nSA) { // angle snapping
654
0
                nStart+=nSA/2_deg100;
655
0
                nStart/=nSA;
656
0
                nStart*=nSA;
657
0
                nStart=NormAngle36000(nStart);
658
0
            }
659
0
        }
660
0
        aP1 = GetAnglePnt(aR,nStart);
661
0
        nEnd=nStart;
662
0
    } else aP1=aCenter;
663
0
    if (rStat.GetPointCount()<=3)
664
0
        return;
665
666
0
    Point aP(rStat.GetPoint(3)-aCenter);
667
0
    if (nWdt>=nHgt) {
668
0
        aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) );
669
0
    } else {
670
0
        aP.setX(BigMulDiv(aP.X(),nHgt,nWdt) );
671
0
    }
672
0
    nEnd=NormAngle36000(GetAngle(aP));
673
0
    if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
674
0
        Degree100 nSA=rStat.GetView()->GetSnapAngle();
675
0
        if (nSA) { // angle snapping
676
0
            nEnd+=nSA/2_deg100;
677
0
            nEnd/=nSA;
678
0
            nEnd*=nSA;
679
0
            nEnd=NormAngle36000(nEnd);
680
0
        }
681
0
    }
682
0
}
683
684
void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat)
685
0
{
686
0
    ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
687
0
    if (pU==nullptr) {
688
0
        pU=new ImpCircUser;
689
0
        rStat.SetUser(std::unique_ptr<ImpCircUser>(pU));
690
0
    }
691
0
    pU->SetCreateParams(rStat);
692
0
}
693
694
bool SdrCircObj::BegCreate(SdrDragStat& rStat)
695
0
{
696
0
    rStat.SetOrtho4Possible();
697
0
    tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
698
0
    aRect1.Normalize();
699
0
    rStat.SetActionRect(aRect1);
700
0
    setRectangle(aRect1);
701
0
    ImpSetCreateParams(rStat);
702
0
    return true;
703
0
}
704
705
bool SdrCircObj::MovCreate(SdrDragStat& rStat)
706
0
{
707
0
    ImpSetCreateParams(rStat);
708
0
    ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
709
0
    rStat.SetActionRect(pU->aR);
710
0
    setRectangle(pU->aR); // for ObjName
711
0
    ImpJustifyRect(maRectangle);
712
0
    m_nStartAngle=pU->nStart;
713
0
    m_nEndAngle=pU->nEnd;
714
0
    SetBoundRectDirty();
715
0
    m_bSnapRectDirty=true;
716
0
    SetXPolyDirty();
717
718
    // #i103058# push current angle settings to ItemSet to
719
    // allow FullDrag visualisation
720
0
    if(rStat.GetPointCount() >= 4)
721
0
    {
722
0
        ImpSetCircInfoToAttr();
723
0
    }
724
725
0
    return true;
726
0
}
727
728
bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
729
0
{
730
0
    ImpSetCreateParams(rStat);
731
0
    ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
732
0
    bool bRet = false;
733
0
    if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full;
734
0
    if (meCircleKind==SdrCircKind::Full) {
735
0
        bRet=rStat.GetPointCount()>=2;
736
0
        if (bRet) {
737
0
            tools::Rectangle aRectangle(pU->aR);
738
0
            ImpJustifyRect(aRectangle);
739
0
            setRectangle(aRectangle);
740
0
        }
741
0
    } else {
742
0
        rStat.SetNoSnap(rStat.GetPointCount()>=2);
743
0
        rStat.SetOrtho4Possible(rStat.GetPointCount()<2);
744
0
        bRet=rStat.GetPointCount()>=4;
745
0
        if (bRet) {
746
0
            tools::Rectangle aRectangle(pU->aR);
747
0
            ImpJustifyRect(aRectangle);
748
0
            setRectangle(aRectangle);
749
0
            m_nStartAngle=pU->nStart;
750
0
            m_nEndAngle=pU->nEnd;
751
0
        }
752
0
    }
753
0
    m_bClosedObj=meCircleKind!=SdrCircKind::Arc;
754
0
    SetBoundAndSnapRectsDirty();
755
0
    SetXPolyDirty();
756
0
    ImpSetCircInfoToAttr();
757
0
    if (bRet)
758
0
        rStat.SetUser(nullptr);
759
0
    return bRet;
760
0
}
761
762
void SdrCircObj::BrkCreate(SdrDragStat& rStat)
763
0
{
764
0
    rStat.SetUser(nullptr);
765
0
}
766
767
bool SdrCircObj::BckCreate(SdrDragStat& rStat)
768
0
{
769
0
    rStat.SetNoSnap(rStat.GetPointCount()>=3);
770
0
    rStat.SetOrtho4Possible(rStat.GetPointCount()<3);
771
0
    return meCircleKind!=SdrCircKind::Full;
772
0
}
773
774
basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
775
0
{
776
0
    const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
777
778
0
    if(rDrag.GetPointCount() < 4)
779
0
    {
780
        // force to OBJ_CIRC to get full visualisation
781
0
        basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(SdrCircKind::Full, pU->aR, pU->nStart, pU->nEnd));
782
783
0
        if(3 == rDrag.GetPointCount())
784
0
        {
785
            // add edge to first point on ellipse
786
0
            basegfx::B2DPolygon aNew;
787
788
0
            aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
789
0
            aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
790
0
            aRetval.append(aNew);
791
0
        }
792
793
0
        return aRetval;
794
0
    }
795
0
    else
796
0
    {
797
0
        return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
798
0
    }
799
0
}
800
801
PointerStyle SdrCircObj::GetCreatePointer() const
802
0
{
803
0
    switch (meCircleKind) {
804
0
        case SdrCircKind::Full: return PointerStyle::DrawEllipse;
805
0
        case SdrCircKind::Section: return PointerStyle::DrawPie;
806
0
        case SdrCircKind::Arc: return PointerStyle::DrawArc;
807
0
        case SdrCircKind::Cut: return PointerStyle::DrawCircleCut;
808
0
        default: break;
809
0
    } // switch
810
0
    return PointerStyle::Cross;
811
0
}
812
813
void SdrCircObj::NbcMove(const Size& aSize)
814
213
{
815
213
    moveRectangle(aSize.Width(), aSize.Height());
816
213
    moveOutRectangle(aSize.Width(), aSize.Height());
817
213
    maSnapRect.Move(aSize);
818
213
    SetXPolyDirty();
819
213
    SetBoundAndSnapRectsDirty(true);
820
213
}
821
822
void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
823
0
{
824
0
    Degree100 nAngle0 = maGeo.m_nRotationAngle;
825
0
    bool bNoShearRota = (maGeo.m_nRotationAngle == 0_deg100 && maGeo.m_nShearAngle == 0_deg100);
826
0
    SdrTextObj::NbcResize(rRef,xFact,yFact);
827
0
    bNoShearRota |= (maGeo.m_nRotationAngle == 0_deg100 && maGeo.m_nShearAngle == 0_deg100);
828
0
    if (meCircleKind!=SdrCircKind::Full) {
829
0
        bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
830
0
        bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
831
0
        if (bXMirr || bYMirr) {
832
            // At bXMirr!=bYMirr we should actually swap both line ends.
833
            // That, however, is pretty bad (because of forced "hard" formatting).
834
            // Alternatively, we could implement a bMirrored flag (maybe even
835
            // a more general one, e. g. for mirrored text, ...).
836
0
            Degree100 nS0=m_nStartAngle;
837
0
            Degree100 nE0=m_nEndAngle;
838
0
            if (bNoShearRota) {
839
                // the RectObj already mirrors at VMirror because of a 180deg rotation
840
0
                if (! (bXMirr && bYMirr)) {
841
0
                    Degree100 nTmp=nS0;
842
0
                    nS0=18000_deg100-nE0;
843
0
                    nE0=18000_deg100-nTmp;
844
0
                }
845
0
            } else { // mirror contorted ellipses
846
0
                if (bXMirr!=bYMirr) {
847
0
                    nS0+=nAngle0;
848
0
                    nE0+=nAngle0;
849
0
                    if (bXMirr) {
850
0
                        Degree100 nTmp=nS0;
851
0
                        nS0=18000_deg100-nE0;
852
0
                        nE0=18000_deg100-nTmp;
853
0
                    }
854
0
                    if (bYMirr) {
855
0
                        Degree100 nTmp=nS0;
856
0
                        nS0=-nE0;
857
0
                        nE0=-nTmp;
858
0
                    }
859
0
                    nS0 -= maGeo.m_nRotationAngle;
860
0
                    nE0 -= maGeo.m_nRotationAngle;
861
0
                }
862
0
            }
863
0
            Degree100 nAngleDif=nE0-nS0;
864
0
            m_nStartAngle=NormAngle36000(nS0);
865
0
            m_nEndAngle  =NormAngle36000(nE0);
866
0
            if (nAngleDif==36000_deg100) m_nEndAngle+=nAngleDif; // full circle
867
0
        }
868
0
    }
869
0
    SetXPolyDirty();
870
0
    ImpSetCircInfoToAttr();
871
0
}
872
873
void SdrCircObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
874
0
{
875
0
    SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
876
0
    SetXPolyDirty();
877
0
    ImpSetCircInfoToAttr();
878
0
}
879
880
void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
881
0
{
882
0
    bool bFreeMirr=meCircleKind!=SdrCircKind::Full;
883
0
    Point aTmpPt1;
884
0
    Point aTmpPt2;
885
0
    if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
886
0
        Point aCenter(getRectangle().Center());
887
0
        tools::Long nWdt = getRectangle().GetWidth() - 1;
888
0
        tools::Long nHgt = getRectangle().GetHeight() - 1;
889
0
        tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
890
        // starting point
891
0
        double a = toRadians(m_nStartAngle);
892
0
        aTmpPt1 = Point(basegfx::fround<tools::Long>(cos(a) * nMaxRad),
893
0
                        basegfx::fround<tools::Long>(-sin(a) * nMaxRad));
894
0
        if (nWdt==0) aTmpPt1.setX(0 );
895
0
        if (nHgt==0) aTmpPt1.setY(0 );
896
0
        aTmpPt1+=aCenter;
897
        // finishing point
898
0
        a = toRadians(m_nEndAngle);
899
0
        aTmpPt2 = Point(basegfx::fround<tools::Long>(cos(a) * nMaxRad),
900
0
                        basegfx::fround<tools::Long>(-sin(a) * nMaxRad));
901
0
        if (nWdt==0) aTmpPt2.setX(0 );
902
0
        if (nHgt==0) aTmpPt2.setY(0 );
903
0
        aTmpPt2+=aCenter;
904
0
        if (maGeo.m_nRotationAngle)
905
0
        {
906
0
            RotatePoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
907
0
            RotatePoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
908
0
        }
909
0
        if (maGeo.m_nShearAngle)
910
0
        {
911
0
            ShearPoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
912
0
            ShearPoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
913
0
        }
914
0
    }
915
0
    SdrTextObj::NbcMirror(rRef1,rRef2);
916
0
    if (meCircleKind!=SdrCircKind::Full) { // adapt starting and finishing angle
917
0
        MirrorPoint(aTmpPt1,rRef1,rRef2);
918
0
        MirrorPoint(aTmpPt2,rRef1,rRef2);
919
        // unrotate:
920
0
        if (maGeo.m_nRotationAngle)
921
0
        {
922
0
            RotatePoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
923
0
            RotatePoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
924
0
        }
925
        // unshear:
926
0
        if (maGeo.m_nShearAngle)
927
0
        {
928
0
            ShearPoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
929
0
            ShearPoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
930
0
        }
931
0
        Point aCenter(getRectangle().Center());
932
0
        aTmpPt1-=aCenter;
933
0
        aTmpPt2-=aCenter;
934
        // because it's mirrored, the angles are swapped, too
935
0
        m_nStartAngle=GetAngle(aTmpPt2);
936
0
        m_nEndAngle  =GetAngle(aTmpPt1);
937
0
        Degree100 nAngleDif=m_nEndAngle-m_nStartAngle;
938
0
        m_nStartAngle=NormAngle36000(m_nStartAngle);
939
0
        m_nEndAngle  =NormAngle36000(m_nEndAngle);
940
0
        if (nAngleDif==36000_deg100) m_nEndAngle+=nAngleDif; // full circle
941
0
    }
942
0
    SetXPolyDirty();
943
0
    ImpSetCircInfoToAttr();
944
0
}
945
946
std::unique_ptr<SdrObjGeoData> SdrCircObj::NewGeoData() const
947
0
{
948
0
    return std::make_unique<SdrCircObjGeoData>();
949
0
}
950
951
void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
952
0
{
953
0
    SdrRectObj::SaveGeoData(rGeo);
954
0
    SdrCircObjGeoData& rCGeo=static_cast<SdrCircObjGeoData&>(rGeo);
955
0
    rCGeo.nStartAngle=m_nStartAngle;
956
0
    rCGeo.nEndAngle  =m_nEndAngle;
957
0
}
958
959
void SdrCircObj::RestoreGeoData(const SdrObjGeoData& rGeo)
960
0
{
961
0
    SdrRectObj::RestoreGeoData(rGeo);
962
0
    const SdrCircObjGeoData& rCGeo=static_cast<const SdrCircObjGeoData&>(rGeo);
963
0
    m_nStartAngle=rCGeo.nStartAngle;
964
0
    m_nEndAngle  =rCGeo.nEndAngle;
965
0
    SetXPolyDirty();
966
0
    ImpSetCircInfoToAttr();
967
0
}
968
969
static void Union(tools::Rectangle& rR, const Point& rP)
970
0
{
971
0
    if (rP.X()<rR.Left  ()) rR.SetLeft(rP.X() );
972
0
    if (rP.X()>rR.Right ()) rR.SetRight(rP.X() );
973
0
    if (rP.Y()<rR.Top   ()) rR.SetTop(rP.Y() );
974
0
    if (rP.Y()>rR.Bottom()) rR.SetBottom(rP.Y() );
975
0
}
976
977
void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
978
473
{
979
473
    rRect = getRectangle();
980
473
    if (meCircleKind!=SdrCircKind::Full) {
981
0
        const Point aPntStart(GetAnglePnt(getRectangle(), m_nStartAngle));
982
0
        const Point aPntEnd(GetAnglePnt(getRectangle(), m_nEndAngle));
983
0
        Degree100 a=m_nStartAngle;
984
0
        Degree100 e=m_nEndAngle;
985
0
        rRect.SetLeft(getRectangle().Right() );
986
0
        rRect.SetRight(getRectangle().Left() );
987
0
        rRect.SetTop(getRectangle().Bottom() );
988
0
        rRect.SetBottom(getRectangle().Top() );
989
0
        Union(rRect,aPntStart);
990
0
        Union(rRect,aPntEnd);
991
0
        if ((a<=18000_deg100 && e>=18000_deg100) || (a>e && (a<=18000_deg100 || e>=18000_deg100))) {
992
0
            Union(rRect, getRectangle().LeftCenter());
993
0
        }
994
0
        if ((a<=27000_deg100 && e>=27000_deg100) || (a>e && (a<=27000_deg100 || e>=27000_deg100))) {
995
0
            Union(rRect, getRectangle().BottomCenter());
996
0
        }
997
0
        if (a>e) {
998
0
            Union(rRect, getRectangle().RightCenter());
999
0
        }
1000
0
        if ((a<=9000_deg100 && e>=9000_deg100) || (a>e && (a<=9000_deg100 || e>=9000_deg100))) {
1001
0
            Union(rRect, getRectangle().TopCenter());
1002
0
        }
1003
0
        if (meCircleKind==SdrCircKind::Section) {
1004
0
            Union(rRect, getRectangle().Center());
1005
0
        }
1006
0
        if (maGeo.m_nRotationAngle)
1007
0
        {
1008
0
            Point aDst(rRect.TopLeft());
1009
0
            aDst -= getRectangle().TopLeft();
1010
0
            Point aDst0(aDst);
1011
0
            RotatePoint(aDst,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1012
0
            aDst-=aDst0;
1013
0
            rRect.Move(aDst.X(),aDst.Y());
1014
0
        }
1015
0
    }
1016
473
    if (maGeo.m_nShearAngle==0_deg100)
1017
473
        return;
1018
1019
0
    tools::Long nDst = basegfx::fround<tools::Long>((rRect.Bottom() - rRect.Top()) * maGeo.mfTanShearAngle);
1020
0
    if (maGeo.m_nShearAngle > 0_deg100)
1021
0
    {
1022
0
        Point aRef(rRect.TopLeft());
1023
0
        rRect.AdjustLeft( -nDst );
1024
0
        Point aTmpPt(rRect.TopLeft());
1025
0
        RotatePoint(aTmpPt, aRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1026
0
        aTmpPt-=rRect.TopLeft();
1027
0
        rRect.Move(aTmpPt.X(),aTmpPt.Y());
1028
0
    } else {
1029
0
        rRect.AdjustRight( -nDst );
1030
0
    }
1031
0
}
1032
1033
void SdrCircObj::RecalcSnapRect()
1034
1.72k
{
1035
1.72k
    if (PaintNeedsXPolyCirc()) {
1036
1.25k
        maSnapRect=GetXPoly().GetBoundRect();
1037
1.25k
    } else {
1038
473
        TakeUnrotatedSnapRect(maSnapRect);
1039
473
    }
1040
1.72k
}
1041
1042
void SdrCircObj::NbcSetSnapRect(const tools::Rectangle& rRect)
1043
1
{
1044
1
    if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle || meCircleKind != SdrCircKind::Full)
1045
0
    {
1046
0
        tools::Rectangle aSR0(GetSnapRect());
1047
0
        tools::Long nWdt0=aSR0.Right()-aSR0.Left();
1048
0
        tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
1049
0
        tools::Long nWdt1=rRect.Right()-rRect.Left();
1050
0
        tools::Long nHgt1=rRect.Bottom()-rRect.Top();
1051
0
        NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
1052
0
        NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
1053
1
    } else {
1054
1
        setRectangle(rRect);
1055
1
        ImpJustifyRect(maRectangle);
1056
1
    }
1057
1
    SetBoundAndSnapRectsDirty();
1058
1
    SetXPolyDirty();
1059
1
    ImpSetCircInfoToAttr();
1060
1
}
1061
1062
sal_uInt32 SdrCircObj::GetSnapPointCount() const
1063
0
{
1064
0
    if (meCircleKind==SdrCircKind::Full) {
1065
0
        return 1;
1066
0
    } else {
1067
0
        return 3;
1068
0
    }
1069
0
}
1070
1071
Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
1072
0
{
1073
0
    switch (i)
1074
0
    {
1075
0
        case 1 : return GetAnglePnt(getRectangle(), m_nStartAngle);
1076
0
        case 2 : return GetAnglePnt(getRectangle(), m_nEndAngle);
1077
0
        default: return getRectangle().Center();
1078
0
    }
1079
0
}
1080
1081
void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1082
0
{
1083
0
    SetXPolyDirty();
1084
0
    SdrRectObj::Notify(rBC,rHint);
1085
0
    ImpSetAttrToCircInfo();
1086
0
}
1087
1088
1089
void SdrCircObj::ImpSetAttrToCircInfo()
1090
199k
{
1091
199k
    const SfxItemSet& rSet = GetObjectItemSet();
1092
199k
    SdrCircKind eNewKind = rSet.Get(SDRATTR_CIRCKIND).GetValue();
1093
1094
199k
    Degree100 nNewStart = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
1095
199k
    Degree100 nNewEnd = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
1096
1097
199k
    bool bKindChg = meCircleKind != eNewKind;
1098
199k
    bool bAngleChg = nNewStart != m_nStartAngle || nNewEnd != m_nEndAngle;
1099
1100
199k
    if(bKindChg || bAngleChg)
1101
0
    {
1102
0
        meCircleKind = eNewKind;
1103
0
        m_nStartAngle = nNewStart;
1104
0
        m_nEndAngle = nNewEnd;
1105
1106
0
        if(bKindChg || (meCircleKind != SdrCircKind::Full && bAngleChg))
1107
0
        {
1108
0
            SetXPolyDirty();
1109
0
            SetBoundAndSnapRectsDirty();
1110
0
        }
1111
0
    }
1112
199k
}
1113
1114
void SdrCircObj::ImpSetCircInfoToAttr()
1115
1
{
1116
1
    const SfxItemSet& rSet = GetObjectItemSet();
1117
1118
1
    SdrCircKind eOldKindA = rSet.Get(SDRATTR_CIRCKIND).GetValue();
1119
1
    Degree100 nOldStartAngle = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
1120
1
    Degree100 nOldEndAngle = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
1121
1122
1
    if(meCircleKind == eOldKindA && m_nStartAngle == nOldStartAngle && m_nEndAngle == nOldEndAngle)
1123
1
        return;
1124
1125
    // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1126
    // setting the item directly is necessary here.
1127
0
    if(meCircleKind != eOldKindA)
1128
0
    {
1129
0
        GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind));
1130
0
    }
1131
1132
0
    if(m_nStartAngle != nOldStartAngle)
1133
0
    {
1134
0
        GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(m_nStartAngle));
1135
0
    }
1136
1137
0
    if(m_nEndAngle != nOldEndAngle)
1138
0
    {
1139
0
        GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(m_nEndAngle));
1140
0
    }
1141
1142
0
    SetXPolyDirty();
1143
0
    ImpSetAttrToCircInfo();
1144
0
}
1145
1146
rtl::Reference<SdrObject> SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
1147
0
{
1148
0
    const bool bFill(meCircleKind != SdrCircKind::Arc);
1149
0
    const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), m_nStartAngle, m_nEndAngle));
1150
0
    rtl::Reference<SdrObject> pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
1151
1152
0
    if(bAddText)
1153
0
    {
1154
0
        pRet = ImpConvertAddText(std::move(pRet), bBezier);
1155
0
    }
1156
1157
0
    return pRet;
1158
0
}
1159
1160
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */