1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
,
51 basegfx::B2DPolygon aRetval
;
53 // TopLeft rounded edge
55 basegfx::utils::createPolygonFromEllipseSegment(
56 basegfx::B2DPoint(rRange
.getMinX(), rRange
.getMinY()),
62 // create Lap topLeft inside
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
84 basegfx::utils::createPolygonFromEllipseSegment(
85 basegfx::B2DPoint(rRange
.getMaxX(), rRange
.getMinY()),
91 // BottomRight rounded edge
93 basegfx::utils::createPolygonFromEllipseSegment(
94 basegfx::B2DPoint(rRange
.getMaxX(), rRange
.getMaxY()),
100 // BottomLeft rounded edge
102 basegfx::utils::createPolygonFromEllipseSegment(
103 basegfx::B2DPoint(rRange
.getMinX(), rRange
.getMaxY()),
109 aRetval
.setClosed(true);
114 // helper primitive to create/show the overlay geometry for a DynamicDiagram
115 class OverlayDiagramPrimitive final
: public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
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
;
128 OverlayDiagramPrimitive(
129 const basegfx::B2DHomMatrix
& rTransformation
,
130 double fDiscreteDistance
,
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
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(
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
)
205 // create outer polygon
207 createRoundedPolygon(
213 // create inner polygon, maybe with Lap
215 createRoundedPolygon(
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(
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
),
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
),
264 rContainer
.push_back(
265 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
266 std::move(aPolygonLapDown
),
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
,
285 Color
const & rColor
)
286 : drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D()
287 , maTransformation(rTransformation
)
288 , mfDiscreteDistance(fDiscreteDistance
)
289 , mfDiscreteGap(fDiscreteGap
)
294 sal_uInt32
OverlayDiagramPrimitive::getPrimitive2DID() const
296 return PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D
;
299 // helper object for DiagramOverlay
300 class OverlayDiagramFrame final
: public sdr::overlay::OverlayObject
303 basegfx::B2DHomMatrix maTransformation
; // object dimensions
304 Color maColor
; // base color
306 virtual drawinglayer::primitive2d::Primitive2DContainer
createOverlayObjectPrimitive2DSequence() override
;
309 explicit OverlayDiagramFrame(
310 const basegfx::B2DHomMatrix
& rTransformation
,
311 Color
const & rColor
);
314 OverlayDiagramFrame::OverlayDiagramFrame(
315 const basegfx::B2DHomMatrix
& rTransformation
,
317 : sdr::overlay::OverlayObject(rColor
)
318 , maTransformation(rTransformation
)
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(
335 8.0, // distance from geometry in pixels
336 8.0, // gap/width of visualization in pixels
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
361 SdrMarkView
* pView
= pHdlList
->GetView();
363 if(!pView
|| pView
->areMarkHandlesHidden())
366 SdrPageView
* pPageView
= pView
->GetSdrPageView();
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();
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(
389 insertNewlyCreatedOverlayObjectForSdrHdl(
390 std::move(pNewOverlayObject
),
391 rPageWindow
.GetObjectContact(),
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: */