Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / diagram / IDiagramHelper.cxx
blobad1db2334d685cce37e48858a4f61bd438c05392
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/diagram/IDiagramHelper.hxx>
21 #include <svx/svdogrp.hxx>
22 #include <svx/svdhdl.hxx>
23 #include <svx/svdmrkv.hxx>
24 #include <svx/svdpagv.hxx>
25 #include <svx/sdrpagewindow.hxx>
26 #include <svx/sdrpaintwindow.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
29 #include <drawinglayer/primitive2d/primitivetools2d.hxx>
30 #include <svx/sdr/overlay/overlaymanager.hxx>
31 #include <drawinglayer/attribute/lineattribute.hxx>
32 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
33 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
34 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
35 #include <comphelper/dispatchcommand.hxx>
36 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
37 #include <officecfg/Office/Common.hxx>
38 #include <svx/strings.hrc>
39 #include <svx/dialmgr.hxx>
41 namespace {
43 // helper to create the geometry for a rounded polygon, maybe
44 // containing a Lap positioned inside top-left for some text
45 basegfx::B2DPolygon createRoundedPolygon(
46 const basegfx::B2DRange& rRange,
47 double fDistance,
48 bool bCreateLap,
49 double fTextWidth)
51 basegfx::B2DPolygon aRetval;
53 // TopLeft rounded edge
54 aRetval.append(
55 basegfx::utils::createPolygonFromEllipseSegment(
56 basegfx::B2DPoint(rRange.getMinX(), rRange.getMinY()),
57 fDistance,
58 fDistance,
59 M_PI * 1.0,
60 M_PI * 1.5));
62 // create Lap topLeft inside
63 if(bCreateLap)
65 const double fLapLeft(rRange.getMinX() + fDistance);
66 double fLapRight(rRange.getMinX() + (rRange.getWidth() * 0.5) - fDistance);
67 const double fLapTop(rRange.getMinY() - fDistance);
68 const double fLapBottom(fLapTop + (fDistance * 2.0));
69 const double fExtendedTextWidth(fTextWidth + (fDistance * 3.0));
71 if(0.0 != fExtendedTextWidth && fLapLeft + fExtendedTextWidth < fLapRight)
73 fLapRight = fLapLeft + fExtendedTextWidth;
76 aRetval.append(basegfx::B2DPoint(fLapLeft, fLapTop));
77 aRetval.append(basegfx::B2DPoint(fLapLeft + (fDistance * 0.5), fLapBottom));
78 aRetval.append(basegfx::B2DPoint(fLapRight - (fDistance * 0.5), fLapBottom));
79 aRetval.append(basegfx::B2DPoint(fLapRight, fLapTop));
82 // TopRight rounded edge
83 aRetval.append(
84 basegfx::utils::createPolygonFromEllipseSegment(
85 basegfx::B2DPoint(rRange.getMaxX(), rRange.getMinY()),
86 fDistance,
87 fDistance,
88 M_PI * 1.5,
89 M_PI * 0.0));
91 // BottomRight rounded edge
92 aRetval.append(
93 basegfx::utils::createPolygonFromEllipseSegment(
94 basegfx::B2DPoint(rRange.getMaxX(), rRange.getMaxY()),
95 fDistance,
96 fDistance,
97 M_PI * 0.0,
98 M_PI * 0.5));
100 // BottomLeft rounded edge
101 aRetval.append(
102 basegfx::utils::createPolygonFromEllipseSegment(
103 basegfx::B2DPoint(rRange.getMinX(), rRange.getMaxY()),
104 fDistance,
105 fDistance,
106 M_PI * 0.5,
107 M_PI * 1.0));
109 aRetval.setClosed(true);
111 return aRetval;
114 // helper primitive to create/show the overlay geometry for a DynamicDiagram
115 class OverlayDiagramPrimitive final : public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
117 private:
118 basegfx::B2DHomMatrix maTransformation; // object dimensions
119 double mfDiscreteDistance; // distance from object in pixels
120 double mfDiscreteGap; // gap/width of visualization in pixels
121 Color maColor; // base color (made lighter/darker as needed, should be system selection color)
123 virtual void create2DDecomposition(
124 drawinglayer::primitive2d::Primitive2DContainer& rContainer,
125 const drawinglayer::geometry::ViewInformation2D& rViewInformation) const override;
127 public:
128 OverlayDiagramPrimitive(
129 const basegfx::B2DHomMatrix& rTransformation,
130 double fDiscreteDistance,
131 double fDiscreteGap,
132 Color const & rColor);
134 virtual sal_uInt32 getPrimitive2DID() const override;
137 void OverlayDiagramPrimitive::create2DDecomposition(
138 drawinglayer::primitive2d::Primitive2DContainer& rContainer,
139 const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) const
141 // get the dimensions. Do *not* take rotation/shear into account,
142 // this is intended to be a pure expanded/frame visualization as
143 // needed in UI for simplified visualization
144 basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
145 aRange.transform(maTransformation);
147 basegfx::B2DPolyPolygon aPolyPolygon;
148 const double fInnerDistance(mfDiscreteDistance * getDiscreteUnit());
149 const double fOuterDistance((mfDiscreteDistance + mfDiscreteGap) * getDiscreteUnit());
150 bool bCreateLap(true);
151 basegfx::B2DPolyPolygon aTextAsPolyPolygon;
152 double fTextWidth(0.0);
154 // initially try to create lap
155 if(bCreateLap)
157 // take a resource text (for now existing one that fits)
158 const OUString aName(SvxResId(RID_STR_DATANAV_EDIT_ELEMENT));
159 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
160 basegfx::B2DPolyPolygonVector aTarget;
161 std::vector<double> aDXArray;
163 // to simplify things for now, do not create a TextSimplePortionPrimitive2D
164 // and needed FontAttribute, just get the TextOutlines as geometry
165 aTextLayouter.getTextOutlines(
166 aTarget,
167 aName,
169 aName.getLength(),
170 aDXArray,
171 {});
173 // put into one PolyPolygon (also simplification - overlapping chars
174 // may create XOR gaps, so these exist for a reason, but low probability)
175 for (auto const& elem : aTarget)
177 aTextAsPolyPolygon.append(elem);
180 // get text dimensions & transform to destination
181 const basegfx::B2DRange aTextRange(aTextAsPolyPolygon.getB2DRange());
182 basegfx::B2DHomMatrix aTextTransform;
184 aTextTransform.translate(aTextRange.getMinX(), aTextRange.getMinY());
185 const double fTargetTextHeight((mfDiscreteDistance + mfDiscreteGap - 2.0) * getDiscreteUnit());
186 const double fTextScale(fTargetTextHeight / aTextRange.getHeight());
187 aTextTransform.scale(fTextScale, fTextScale);
188 aTextTransform.translate(
189 aRange.getMinX() + (fInnerDistance * 2.0),
190 aRange.getMinY() + fTargetTextHeight + (fOuterDistance - fInnerDistance) - (2.0 * getDiscreteUnit()));
191 aTextAsPolyPolygon.transform(aTextTransform);
193 // check text size/position
194 fTextWidth = aTextRange.getWidth() * fTextScale;
195 const double fLapLeft(aRange.getMinX() + fInnerDistance);
196 const double fLapRight(aRange.getMinX() + (aRange.getWidth() * 0.5) - fInnerDistance);
198 // if text is too big, do not create a Lap at all
199 // to avoid trouble. It is expected that the user keeps
200 // the object he works with big enough to do useful actions
201 if(fTextWidth + (4.0 * getDiscreteUnit()) > fLapRight - fLapLeft)
202 bCreateLap = false;
205 // create outer polygon
206 aPolyPolygon.append(
207 createRoundedPolygon(
208 aRange,
209 fOuterDistance,
210 false,
211 0.0));
213 // create inner polygon, maybe with Lap
214 aPolyPolygon.append(
215 createRoundedPolygon(
216 aRange,
217 fInnerDistance,
218 bCreateLap,
219 fTextWidth));
221 Color aFillColor(maColor);
222 Color aLineColor(maColor);
224 aFillColor.IncreaseLuminance(10);
225 aLineColor.DecreaseLuminance(30);
227 const drawinglayer::attribute::LineAttribute aLineAttribute(
228 aLineColor.getBColor(),
229 1.0 * getDiscreteUnit());
231 // filled polygon as BG (may get transparence for better look ?)
232 rContainer.push_back(
233 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
234 aPolyPolygon,
235 aFillColor.getBColor()));
237 // outline polygon for visibility (may be accentuated shaded
238 // top/left, would require alternative creation)
239 rContainer.push_back(
240 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
241 std::move(aPolyPolygon),
242 aLineAttribute));
244 // top-left line pattern (as grep-here-sign to signal
245 // that this construct may be also dragged by the user)
246 const double fLapLeft(aRange.getMinX() + fInnerDistance);
247 const double fLapRight(aRange.getMinX() + (aRange.getWidth() * 0.5) - fInnerDistance);
248 const double fLapUp(aRange.getMinY() - ((mfDiscreteDistance + mfDiscreteDistance * 0.666) * getDiscreteUnit()));
249 const double fLapDown(aRange.getMinY() - ((mfDiscreteDistance + mfDiscreteDistance * 0.333) * getDiscreteUnit()));
250 basegfx::B2DPolygon aPolygonLapUp;
251 aPolygonLapUp.append(basegfx::B2DPoint(fLapLeft, fLapUp));
252 aPolygonLapUp.append(basegfx::B2DPoint(fLapRight, fLapUp));
253 basegfx::B2DPolygon aPolygonLapDown;
254 aPolygonLapDown.append(basegfx::B2DPoint(fLapLeft, fLapDown));
255 aPolygonLapDown.append(basegfx::B2DPoint(fLapRight, fLapDown));
256 drawinglayer::attribute::StrokeAttribute aStrokeAttribute({ 2.0 * getDiscreteUnit(), 2.0 * getDiscreteUnit() });
258 rContainer.push_back(
259 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
260 std::move(aPolygonLapUp),
261 aLineAttribute,
262 aStrokeAttribute));
264 rContainer.push_back(
265 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
266 std::move(aPolygonLapDown),
267 aLineAttribute,
268 std::move(aStrokeAttribute)));
270 // add text last. May use darker text color, go for same color
271 // as accentuation line for now
272 if(bCreateLap && 0 != aTextAsPolyPolygon.count())
274 rContainer.push_back(
275 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
276 std::move(aTextAsPolyPolygon),
277 aLineColor.getBColor()));
281 OverlayDiagramPrimitive::OverlayDiagramPrimitive(
282 const basegfx::B2DHomMatrix& rTransformation,
283 double fDiscreteDistance,
284 double fDiscreteGap,
285 Color const & rColor)
286 : drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D()
287 , maTransformation(rTransformation)
288 , mfDiscreteDistance(fDiscreteDistance)
289 , mfDiscreteGap(fDiscreteGap)
290 , maColor(rColor)
294 sal_uInt32 OverlayDiagramPrimitive::getPrimitive2DID() const
296 return PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D;
299 // helper object for DiagramOverlay
300 class OverlayDiagramFrame final : public sdr::overlay::OverlayObject
302 private:
303 basegfx::B2DHomMatrix maTransformation; // object dimensions
304 Color maColor; // base color
306 virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
308 public:
309 explicit OverlayDiagramFrame(
310 const basegfx::B2DHomMatrix& rTransformation,
311 Color const & rColor);
314 OverlayDiagramFrame::OverlayDiagramFrame(
315 const basegfx::B2DHomMatrix& rTransformation,
316 const Color& rColor)
317 : sdr::overlay::OverlayObject(rColor)
318 , maTransformation(rTransformation)
319 , maColor(rColor)
323 drawinglayer::primitive2d::Primitive2DContainer OverlayDiagramFrame::createOverlayObjectPrimitive2DSequence()
325 drawinglayer::primitive2d::Primitive2DContainer aReturnContainer;
327 if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
328 return aReturnContainer;
330 if (getOverlayManager())
332 aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer {
333 new OverlayDiagramPrimitive(
334 maTransformation,
335 8.0, // distance from geometry in pixels
336 8.0, // gap/width of visualization in pixels
337 maColor) };
340 return aReturnContainer;
343 } // end of anonymous namespace
345 namespace svx { namespace diagram {
347 void DiagramFrameHdl::clicked(const Point& /*rPnt*/)
349 // this may check for a direct hit at the text later
350 // and only then take action. That would require
351 // to evaluate & keep that (maybe during creation).
352 // For now, just trigger to open the Dialog
353 comphelper::dispatchCommand(".uno:EditDiagram", {});
356 void DiagramFrameHdl::CreateB2dIAObject()
358 // first throw away old one
359 GetRidOfIAObject();
361 SdrMarkView* pView = pHdlList->GetView();
363 if(!pView || pView->areMarkHandlesHidden())
364 return;
366 SdrPageView* pPageView = pView->GetSdrPageView();
368 if(!pPageView)
369 return;
371 for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
373 const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
375 if(rPageWindow.GetPaintWindow().OutputToWindow())
377 const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
378 if (xManager.is())
380 OutputDevice& rOutDev(rPageWindow.GetPaintWindow().GetOutputDevice());
381 const StyleSettings& rStyles(rOutDev.GetSettings().GetStyleSettings());
382 Color aFillColor(rStyles.GetHighlightColor());
383 std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
384 new OverlayDiagramFrame(
385 maTransformation,
386 aFillColor));
388 // OVERLAYMANAGER
389 insertNewlyCreatedOverlayObjectForSdrHdl(
390 std::move(pNewOverlayObject),
391 rPageWindow.GetObjectContact(),
392 *xManager);
398 DiagramFrameHdl::DiagramFrameHdl(const basegfx::B2DHomMatrix& rTransformation)
399 : SdrHdl(Point(), SdrHdlKind::Move)
400 , maTransformation(rTransformation)
404 IDiagramHelper::IDiagramHelper()
405 : mbUseDiagramThemeData(false)
406 , mbUseDiagramModelData(true)
407 , mbForceThemePtrRecreation(false)
411 IDiagramHelper::~IDiagramHelper() {}
413 void IDiagramHelper::anchorToSdrObjGroup(SdrObjGroup& rTarget)
415 rTarget.mp_DiagramHelper.reset(this);
418 void IDiagramHelper::AddAdditionalVisualization(const SdrObjGroup& rTarget, SdrHdlList& rHdlList)
420 // create an extra frame visualization here
421 basegfx::B2DHomMatrix aTransformation;
422 basegfx::B2DPolyPolygon aPolyPolygon;
423 rTarget.TRGetBaseGeometry(aTransformation, aPolyPolygon);
425 std::unique_ptr<SdrHdl> pHdl(new DiagramFrameHdl(aTransformation));
426 rHdlList.AddHdl(std::move(pHdl));
429 }} // end of namespace
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */