/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: */ |