LibreOffice Module drawinglayer (master) 1
controlprimitive2d.cxx
Go to the documentation of this file.
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
21#include <com/sun/star/awt/XWindow.hpp>
22#include <com/sun/star/awt/XVclWindowPeer.hpp>
23#include <com/sun/star/beans/XPropertySet.hpp>
25#include <com/sun/star/awt/XControl.hpp>
26#include <com/sun/star/uno/XComponentContext.hpp>
28#include <utility>
29#include <rtl/ustrbuf.hxx>
30#include <vcl/virdev.hxx>
31#include <vcl/svapp.hxx>
32#include <com/sun/star/awt/PosSize.hpp>
33#include <vcl/bitmapex.hxx>
41#include <vcl/window.hxx>
44
45using namespace com::sun::star;
46
48{
50 {
51 if(mxXControl.is() || !getControlModel().is())
52 return;
53
54 uno::Reference< beans::XPropertySet > xSet(getControlModel(), uno::UNO_QUERY);
55
56 if(!xSet.is())
57 return;
58
59 uno::Any aValue(xSet->getPropertyValue("DefaultControl"));
60 OUString aUnoControlTypeName;
61
62 if(!(aValue >>= aUnoControlTypeName))
63 return;
64
65 if(aUnoControlTypeName.isEmpty())
66 return;
67
68 uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
69 uno::Reference< awt::XControl > xXControl(
70 xContext->getServiceManager()->createInstanceWithContext(aUnoControlTypeName, xContext), uno::UNO_QUERY);
71
72 if(xXControl.is())
73 {
74 xXControl->setModel(getControlModel());
75
76 // remember XControl
77 mxXControl = xXControl;
78 }
79 }
80
82 {
84 const uno::Reference< awt::XControl >& rXControl(getXControl());
85
86 if(rXControl.is())
87 {
88 uno::Reference< awt::XWindow > xControlWindow(rXControl, uno::UNO_QUERY);
89
90 if(xControlWindow.is())
91 {
92 // get decomposition to get size
93 basegfx::B2DVector aScale, aTranslate;
94 double fRotate, fShearX;
95 getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
96
97 // get absolute discrete size (no mirror or rotate here)
98 aScale = basegfx::absolute(aScale);
99 basegfx::B2DVector aDiscreteSize(rViewInformation.getObjectToViewTransformation() * aScale);
100
101 // limit to a maximum square size, e.g. 300x150 pixels (45000)
103 const double fDiscreteQuadratic(aDiscreteSize.getX() * aDiscreteSize.getY());
104 const bool bScaleUsed(fDiscreteQuadratic > fDiscreteMax);
105 double fFactor(1.0);
106
107 if(bScaleUsed)
108 {
109 // get factor and adapt to scaled size
110 fFactor = sqrt(fDiscreteMax / fDiscreteQuadratic);
111 aDiscreteSize *= fFactor;
112 }
113
114 // go to integer
115 const sal_Int32 nSizeX(basegfx::fround(aDiscreteSize.getX()));
116 const sal_Int32 nSizeY(basegfx::fround(aDiscreteSize.getY()));
117
118 if(nSizeX > 0 && nSizeY > 0)
119 {
120 // prepare VirtualDevice
122 const Size aSizePixel(nSizeX, nSizeY);
123 aVirtualDevice->SetOutputSizePixel(aSizePixel);
124
125 // set size at control
126 xControlWindow->setPosSize(0, 0, nSizeX, nSizeY, awt::PosSize::POSSIZE);
127
128 // get graphics and view
129 uno::Reference< awt::XGraphics > xGraphics(aVirtualDevice->CreateUnoGraphics());
130 uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY);
131
132 if(xGraphics.is() && xControlView.is())
133 {
134 // link graphics and view
135 xControlView->setGraphics(xGraphics);
136
137 { // #i93162# For painting the control setting a Zoom (using setZoom() at the xControlView)
138 // is needed to define the font size. Normally this is done in
139 // ViewObjectContactOfUnoControl::createPrimitive2DSequence by using positionControlForPaint().
140 // For some reason the difference between MapUnit::MapTwipS and MapUnit::Map100thMM still plays
141 // a role there so that for Draw/Impress/Calc (the MapUnit::Map100thMM users) i need to set a zoom
142 // here, too. The factor includes the needed scale, but is calculated by pure comparisons. It
143 // is somehow related to the twips/100thmm relationship.
144 bool bUserIs100thmm(false);
145 const uno::Reference< awt::XControl > xControl(xControlView, uno::UNO_QUERY);
146
147 if(xControl.is())
148 {
149 uno::Reference<awt::XWindowPeer> xWindowPeer(xControl->getPeer());
150 if (xWindowPeer)
151 {
152 uno::Reference<awt::XVclWindowPeer> xPeerProps(xWindowPeer, uno::UNO_QUERY_THROW);
153 uno::Any aAny = xPeerProps->getProperty("ParentIs100thmm"); // see VCLXWindow::getProperty
154 aAny >>= bUserIs100thmm;
155 }
156 }
157
158 if(bUserIs100thmm)
159 {
160 // calc screen zoom for text display. fFactor is already added indirectly in aDiscreteSize
161 basegfx::B2DVector aScreenZoom(
162 basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : aDiscreteSize.getX() / aScale.getX(),
163 basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : aDiscreteSize.getY() / aScale.getY());
164 static const double fZoomScale(28.0); // do not ask for this constant factor, but it gets the zoom right
165 aScreenZoom *= fZoomScale;
166
167 // set zoom at control view for text scaling
168 xControlView->setZoom(static_cast<float>(aScreenZoom.getX()), static_cast<float>(aScreenZoom.getY()));
169 }
170 }
171
172 try
173 {
174 // try to paint it to VirtualDevice
175 xControlView->draw(0, 0);
176
177 // get bitmap
178 const BitmapEx aContent(aVirtualDevice->GetBitmapEx(Point(), aSizePixel));
179
180 // to avoid scaling, use the Bitmap pixel size as primitive size
181 const Size aBitmapSize(aContent.GetSizePixel());
182 basegfx::B2DVector aBitmapSizeLogic(
183 rViewInformation.getInverseObjectToViewTransformation() *
184 basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1));
185
186 if(bScaleUsed)
187 {
188 // if scaled adapt to scaled size
189 aBitmapSizeLogic /= fFactor;
190 }
191
192 // short form for scale and translate transformation
194 aBitmapSizeLogic.getX(), aBitmapSizeLogic.getY(), aTranslate.getX(), aTranslate.getY()));
195
196 // create primitive
197 xRetval = new BitmapPrimitive2D(
198 aContent,
199 aBitmapTransform);
200 }
201 catch( const uno::Exception& )
202 {
203 DBG_UNHANDLED_EXCEPTION("drawinglayer");
204 }
205 }
206 }
207 }
208 }
209
210 return xRetval;
211 }
212
214 {
215 // create a gray placeholder hairline polygon in object size
216 basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0);
217 aObjectRange.transform(getTransform());
219 const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0);
220
221 // The replacement object may also get a text like 'empty group' here later
222 Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(std::move(aOutline), aGrayTone));
223
224 return xRetval;
225 }
226
228 {
229 // try to create a bitmap decomposition. If that fails for some reason,
230 // at least create a replacement decomposition.
231 Primitive2DReference xReference(createBitmapDecomposition(rViewInformation));
232
233 if(!xReference.is())
234 {
235 xReference = createPlaceholderDecomposition();
236 }
237
238 rContainer.push_back(xReference);
239 }
240
242 basegfx::B2DHomMatrix aTransform,
243 uno::Reference< awt::XControlModel > xControlModel,
244 uno::Reference<awt::XControl> xXControl,
245 ::std::u16string_view const rTitle,
246 ::std::u16string_view const rDescription)
247 : maTransform(std::move(aTransform)),
248 mxControlModel(std::move(xControlModel)),
249 mxXControl(std::move(xXControl))
250 {
251 ::rtl::OUStringBuffer buf(rTitle);
252 if (!rTitle.empty() && !rDescription.empty())
253 {
254 buf.append(" - ");
255 }
256 buf.append(rDescription);
257 m_AltText = buf.makeStringAndClear();
258 }
259
260 const uno::Reference< awt::XControl >& ControlPrimitive2D::getXControl() const
261 {
262 if(!mxXControl.is())
263 {
264 const_cast< ControlPrimitive2D* >(this)->createXControl();
265 }
266
267 return mxXControl;
268 }
269
271 {
272 // use base class compare operator
273 if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
274 return false;
275
276 const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive);
277
278 if(getTransform() != rCompare.getTransform())
279 return false;
280
281 // check if ControlModel references both are/are not
282 if (getControlModel().is() != rCompare.getControlModel().is())
283 return false;
284
285 if(getControlModel().is())
286 {
287 // both exist, check for equality
288 if (getControlModel() != rCompare.getControlModel())
289 return false;
290 }
291
292 // check if XControl references both are/are not
293 if (getXControl().is() != rCompare.getXControl().is())
294 return false;
295
296 if(getXControl().is())
297 {
298 // both exist, check for equality
299 if (getXControl() != rCompare.getXControl())
300 return false;
301 }
302
303 return true;
304 }
305
307 {
308 // simply derivate from unit range
309 basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
310 aRetval.transform(getTransform());
311 return aRetval;
312 }
313
315 {
316 // this primitive is view-dependent related to the scaling. If scaling has changed,
317 // destroy existing decomposition. To detect change, use size of unit size in view coordinates
318 const basegfx::B2DVector aNewScaling(rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
319
320 if(!getBuffered2DDecomposition().empty())
321 {
322 if(!maLastViewScaling.equal(aNewScaling))
323 {
324 // conditions of last local decomposition have changed, delete
326 }
327 }
328
329 if(getBuffered2DDecomposition().empty())
330 {
331 // remember ViewTransformation
332 const_cast< ControlPrimitive2D* >(this)->maLastViewScaling = aNewScaling;
333 }
334
335 // use parent implementation
337 }
338
339 // provide unique ID
341 {
343 }
344
345} // end of namespace
346
347/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static OutputDevice * GetDefaultDevice()
const Size & GetSizePixel() const
constexpr tools::Long getHeight() const
constexpr tools::Long getWidth() const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
bool equal(const Tuple2D< TYPE > &rTup) const
TYPE getX() const
TYPE getY() const
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
const basegfx::B2DHomMatrix & getObjectToViewTransformation() const
On-demand prepared Object to View transformation and its inverse for convenience.
const Primitive2DContainer & getBuffered2DDecomposition() const
access methods to maBuffered2DDecomposition.
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
The getDecomposition default implementation will on demand use create2DDecomposition() if maBuffered2...
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition
const css::uno::Reference< css::awt::XControl > & getXControl() const
mxControl access.
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
basegfx::B2DVector maLastViewScaling
the last used scaling, used from getDecomposition for buffering
ControlPrimitive2D(basegfx::B2DHomMatrix aTransform, css::uno::Reference< css::awt::XControlModel > xControlModel, css::uno::Reference< css::awt::XControl > xXControl, ::std::u16string_view rTitle, ::std::u16string_view rDescription)
constructor with an optional XControl as parameter to allow to hand it over at incarnation time if it...
OUString m_AltText
yet another special snowflake way to generate PDF Alt text
const basegfx::B2DHomMatrix & getTransform() const
data read access
Primitive2DReference createPlaceholderDecomposition() const
css::uno::Reference< css::awt::XControl > mxXControl
the created and cached awt::XControl
virtual void get2DDecomposition(Primitive2DDecompositionVisitor &rVisitor, const geometry::ViewInformation2D &rViewInformation) const override
Override standard getDecomposition to be view-dependent here.
void createXControl()
used from getXControl() to create a local awt::XControl which is remembered in mxXControl and from th...
Primitive2DReference createBitmapDecomposition(const geometry::ViewInformation2D &rViewInformation) const
single local decompositions, used from create2DDecomposition()
const css::uno::Reference< css::awt::XControlModel > & getControlModel() const
#define DBG_UNHANDLED_EXCEPTION(...)
#define PRIMITIVE2D_ID_CONTROLPRIMITIVE2D
basegfx::B2DHomMatrix maTransform
the geometric definition
sal_uInt32 GetQuadraticFormControlRenderLimit()
bool equalZero(const T &rfVal)
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
B2DTuple absolute(const B2DTuple &rTup)
B2IRange fround(const B2DRange &rRange)