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 <drawinglayer/primitive2d/groupprimitive2d.hxx>
36 #include <comphelper/dispatchcommand.hxx>
37 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
38 #include <officecfg/Office/Common.hxx>
39 #include <svx/strings.hrc>
40 #include <svx/dialmgr.hxx>
44 // helper to create the geometry for a rounded polygon, maybe
45 // containing a Lap positioned inside top-left for some text
46 basegfx::B2DPolygon
createRoundedPolygon(
47 const basegfx::B2DRange
& rRange
,
52 basegfx::B2DPolygon aRetval
;
54 // TopLeft rounded edge
56 basegfx::utils::createPolygonFromEllipseSegment(
57 basegfx::B2DPoint(rRange
.getMinX(), rRange
.getMinY()),
63 // create Lap topLeft inside
66 const double fLapLeft(rRange
.getMinX() + fDistance
);
67 double fLapRight(rRange
.getMinX() + (rRange
.getWidth() * 0.5) - fDistance
);
68 const double fLapTop(rRange
.getMinY() - fDistance
);
69 const double fLapBottom(fLapTop
+ (fDistance
* 2.0));
70 const double fExtendedTextWidth(fTextWidth
+ (fDistance
* 3.0));
72 if(0.0 != fExtendedTextWidth
&& fLapLeft
+ fExtendedTextWidth
< fLapRight
)
74 fLapRight
= fLapLeft
+ fExtendedTextWidth
;
77 aRetval
.append(basegfx::B2DPoint(fLapLeft
, fLapTop
));
78 aRetval
.append(basegfx::B2DPoint(fLapLeft
+ (fDistance
* 0.5), fLapBottom
));
79 aRetval
.append(basegfx::B2DPoint(fLapRight
- (fDistance
* 0.5), fLapBottom
));
80 aRetval
.append(basegfx::B2DPoint(fLapRight
, fLapTop
));
83 // TopRight rounded edge
85 basegfx::utils::createPolygonFromEllipseSegment(
86 basegfx::B2DPoint(rRange
.getMaxX(), rRange
.getMinY()),
92 // BottomRight rounded edge
94 basegfx::utils::createPolygonFromEllipseSegment(
95 basegfx::B2DPoint(rRange
.getMaxX(), rRange
.getMaxY()),
101 // BottomLeft rounded edge
103 basegfx::utils::createPolygonFromEllipseSegment(
104 basegfx::B2DPoint(rRange
.getMinX(), rRange
.getMaxY()),
110 aRetval
.setClosed(true);
115 // helper primitive to create/show the overlay geometry for a DynamicDiagram
116 class OverlayDiagramPrimitive final
: public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
119 basegfx::B2DHomMatrix maTransformation
; // object dimensions
120 double mfDiscreteDistance
; // distance from object in pixels
121 double mfDiscreteGap
; // gap/width of visualization in pixels
122 Color maColor
; // base color (made lighter/darker as needed, should be system selection color)
124 virtual drawinglayer::primitive2d::Primitive2DReference
create2DDecomposition(
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 drawinglayer::primitive2d::Primitive2DReference
OverlayDiagramPrimitive::create2DDecomposition(
138 const drawinglayer::geometry::ViewInformation2D
& /*rViewInformation*/) const
140 // get the dimensions. Do *not* take rotation/shear into account,
141 // this is intended to be a pure expanded/frame visualization as
142 // needed in UI for simplified visualization
143 basegfx::B2DRange
aRange(0.0, 0.0, 1.0, 1.0);
144 aRange
.transform(maTransformation
);
146 basegfx::B2DPolyPolygon aPolyPolygon
;
147 const double fInnerDistance(mfDiscreteDistance
* getDiscreteUnit());
148 const double fOuterDistance((mfDiscreteDistance
+ mfDiscreteGap
) * getDiscreteUnit());
149 bool bCreateLap(true);
150 basegfx::B2DPolyPolygon aTextAsPolyPolygon
;
151 double fTextWidth(0.0);
153 // initially try to create lap
156 // take a resource text (for now existing one that fits)
157 const OUString
aName(SvxResId(RID_STR_DATANAV_EDIT_ELEMENT
));
158 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter
;
159 basegfx::B2DPolyPolygonVector aTarget
;
160 std::vector
<double> aDXArray
;
162 // to simplify things for now, do not create a TextSimplePortionPrimitive2D
163 // and needed FontAttribute, just get the TextOutlines as geometry
164 aTextLayouter
.getTextOutlines(
172 // put into one PolyPolygon (also simplification - overlapping chars
173 // may create XOR gaps, so these exist for a reason, but low probability)
174 for (auto const& elem
: aTarget
)
176 aTextAsPolyPolygon
.append(elem
);
179 // get text dimensions & transform to destination
180 const basegfx::B2DRange
aTextRange(aTextAsPolyPolygon
.getB2DRange());
181 basegfx::B2DHomMatrix aTextTransform
;
183 aTextTransform
.translate(aTextRange
.getMinX(), aTextRange
.getMinY());
184 const double fTargetTextHeight((mfDiscreteDistance
+ mfDiscreteGap
- 2.0) * getDiscreteUnit());
185 const double fTextScale(fTargetTextHeight
/ aTextRange
.getHeight());
186 aTextTransform
.scale(fTextScale
, fTextScale
);
187 aTextTransform
.translate(
188 aRange
.getMinX() + (fInnerDistance
* 2.0),
189 aRange
.getMinY() + fTargetTextHeight
+ (fOuterDistance
- fInnerDistance
) - (2.0 * getDiscreteUnit()));
190 aTextAsPolyPolygon
.transform(aTextTransform
);
192 // check text size/position
193 fTextWidth
= aTextRange
.getWidth() * fTextScale
;
194 const double fLapLeft(aRange
.getMinX() + fInnerDistance
);
195 const double fLapRight(aRange
.getMinX() + (aRange
.getWidth() * 0.5) - fInnerDistance
);
197 // if text is too big, do not create a Lap at all
198 // to avoid trouble. It is expected that the user keeps
199 // the object he works with big enough to do useful actions
200 if(fTextWidth
+ (4.0 * getDiscreteUnit()) > fLapRight
- fLapLeft
)
204 // create outer polygon
206 createRoundedPolygon(
212 // create inner polygon, maybe with Lap
214 createRoundedPolygon(
220 Color
aFillColor(maColor
);
221 Color
aLineColor(maColor
);
223 aFillColor
.IncreaseLuminance(10);
224 aLineColor
.DecreaseLuminance(30);
226 const drawinglayer::attribute::LineAttribute
aLineAttribute(
227 aLineColor
.getBColor(),
228 1.0 * getDiscreteUnit());
230 drawinglayer::primitive2d::Primitive2DContainer aContainer
;
232 // filled polygon as BG (may get transparence for better look ?)
233 aContainer
.push_back(
234 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
236 aFillColor
.getBColor()));
238 // outline polygon for visibility (may be accentuated shaded
239 // top/left, would require alternative creation)
240 aContainer
.push_back(
241 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
242 std::move(aPolyPolygon
),
245 // top-left line pattern (as grep-here-sign to signal
246 // that this construct may be also dragged by the user)
247 const double fLapLeft(aRange
.getMinX() + fInnerDistance
);
248 const double fLapRight(aRange
.getMinX() + (aRange
.getWidth() * 0.5) - fInnerDistance
);
249 const double fLapUp(aRange
.getMinY() - ((mfDiscreteDistance
+ mfDiscreteDistance
* 0.666) * getDiscreteUnit()));
250 const double fLapDown(aRange
.getMinY() - ((mfDiscreteDistance
+ mfDiscreteDistance
* 0.333) * getDiscreteUnit()));
251 basegfx::B2DPolygon aPolygonLapUp
;
252 aPolygonLapUp
.append(basegfx::B2DPoint(fLapLeft
, fLapUp
));
253 aPolygonLapUp
.append(basegfx::B2DPoint(fLapRight
, fLapUp
));
254 basegfx::B2DPolygon aPolygonLapDown
;
255 aPolygonLapDown
.append(basegfx::B2DPoint(fLapLeft
, fLapDown
));
256 aPolygonLapDown
.append(basegfx::B2DPoint(fLapRight
, fLapDown
));
257 drawinglayer::attribute::StrokeAttribute
aStrokeAttribute({ 2.0 * getDiscreteUnit(), 2.0 * getDiscreteUnit() });
259 aContainer
.push_back(
260 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
261 std::move(aPolygonLapUp
),
265 aContainer
.push_back(
266 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
267 std::move(aPolygonLapDown
),
269 std::move(aStrokeAttribute
)));
271 // add text last. May use darker text color, go for same color
272 // as accentuation line for now
273 if(bCreateLap
&& 0 != aTextAsPolyPolygon
.count())
275 aContainer
.push_back(
276 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
277 std::move(aTextAsPolyPolygon
),
278 aLineColor
.getBColor()));
280 return new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer
));
283 OverlayDiagramPrimitive::OverlayDiagramPrimitive(
284 const basegfx::B2DHomMatrix
& rTransformation
,
285 double fDiscreteDistance
,
287 Color
const & rColor
)
288 : drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D()
289 , maTransformation(rTransformation
)
290 , mfDiscreteDistance(fDiscreteDistance
)
291 , mfDiscreteGap(fDiscreteGap
)
296 sal_uInt32
OverlayDiagramPrimitive::getPrimitive2DID() const
298 return PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D
;
301 // helper object for DiagramOverlay
302 class OverlayDiagramFrame final
: public sdr::overlay::OverlayObject
305 basegfx::B2DHomMatrix maTransformation
; // object dimensions
306 Color maColor
; // base color
308 virtual drawinglayer::primitive2d::Primitive2DContainer
createOverlayObjectPrimitive2DSequence() override
;
311 explicit OverlayDiagramFrame(
312 const basegfx::B2DHomMatrix
& rTransformation
,
313 Color
const & rColor
);
316 OverlayDiagramFrame::OverlayDiagramFrame(
317 const basegfx::B2DHomMatrix
& rTransformation
,
319 : sdr::overlay::OverlayObject(rColor
)
320 , maTransformation(rTransformation
)
325 drawinglayer::primitive2d::Primitive2DContainer
OverlayDiagramFrame::createOverlayObjectPrimitive2DSequence()
327 drawinglayer::primitive2d::Primitive2DContainer aReturnContainer
;
329 if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
330 return aReturnContainer
;
332 if (getOverlayManager())
334 aReturnContainer
= drawinglayer::primitive2d::Primitive2DContainer
{
335 new OverlayDiagramPrimitive(
337 8.0, // distance from geometry in pixels
338 8.0, // gap/width of visualization in pixels
342 return aReturnContainer
;
345 } // end of anonymous namespace
347 namespace svx
{ namespace diagram
{
349 void DiagramFrameHdl::clicked(const Point
& /*rPnt*/)
351 // this may check for a direct hit at the text later
352 // and only then take action. That would require
353 // to evaluate & keep that (maybe during creation).
354 // For now, just trigger to open the Dialog
355 comphelper::dispatchCommand(u
".uno:EditDiagram"_ustr
, {});
358 void DiagramFrameHdl::CreateB2dIAObject()
360 // first throw away old one
363 SdrMarkView
* pView
= m_pHdlList
->GetView();
365 if(!pView
|| pView
->areMarkHandlesHidden())
368 SdrPageView
* pPageView
= pView
->GetSdrPageView();
373 for(sal_uInt32
b(0); b
< pPageView
->PageWindowCount(); b
++)
375 const SdrPageWindow
& rPageWindow
= *pPageView
->GetPageWindow(b
);
377 if(rPageWindow
.GetPaintWindow().OutputToWindow())
379 const rtl::Reference
< sdr::overlay::OverlayManager
>& xManager
= rPageWindow
.GetOverlayManager();
382 OutputDevice
& rOutDev(rPageWindow
.GetPaintWindow().GetOutputDevice());
383 const StyleSettings
& rStyles(rOutDev
.GetSettings().GetStyleSettings());
384 Color
aFillColor(rStyles
.GetHighlightColor());
385 std::unique_ptr
<sdr::overlay::OverlayObject
> pNewOverlayObject(
386 new OverlayDiagramFrame(
391 insertNewlyCreatedOverlayObjectForSdrHdl(
392 std::move(pNewOverlayObject
),
393 rPageWindow
.GetObjectContact(),
400 DiagramFrameHdl::DiagramFrameHdl(const basegfx::B2DHomMatrix
& rTransformation
)
401 : SdrHdl(Point(), SdrHdlKind::Move
)
402 , maTransformation(rTransformation
)
406 IDiagramHelper::IDiagramHelper(bool bSelfCreated
)
407 : mbUseDiagramThemeData(false)
408 , mbUseDiagramModelData(true)
409 , mbForceThemePtrRecreation(false)
410 , mbSelfCreated(bSelfCreated
)
414 IDiagramHelper::~IDiagramHelper() {}
416 void IDiagramHelper::anchorToSdrObjGroup(SdrObjGroup
& rTarget
)
418 rTarget
.mp_DiagramHelper
.reset(this);
421 void IDiagramHelper::AddAdditionalVisualization(const SdrObjGroup
& rTarget
, SdrHdlList
& rHdlList
)
423 // create an extra frame visualization here
424 basegfx::B2DHomMatrix aTransformation
;
425 basegfx::B2DPolyPolygon aPolyPolygon
;
426 rTarget
.TRGetBaseGeometry(aTransformation
, aPolyPolygon
);
428 std::unique_ptr
<SdrHdl
> pHdl(new DiagramFrameHdl(aTransformation
));
429 rHdlList
.AddHdl(std::move(pHdl
));
432 }} // end of namespace
434 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */