/src/libreoffice/drawinglayer/source/primitive2d/controlprimitive2d.cxx
Line | Count | Source |
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 <drawinglayer/primitive2d/controlprimitive2d.hxx> |
21 | | #include <com/sun/star/awt/XWindow.hpp> |
22 | | #include <com/sun/star/awt/XVclWindowPeer.hpp> |
23 | | #include <com/sun/star/beans/XPropertySet.hpp> |
24 | | #include <comphelper/processfactory.hxx> |
25 | | #include <com/sun/star/awt/XControl.hpp> |
26 | | #include <com/sun/star/uno/XComponentContext.hpp> |
27 | | #include <drawinglayer/geometry/viewinformation2d.hxx> |
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 <com/sun/star/awt/XWindow2.hpp> |
34 | | #include <vcl/bitmap.hxx> |
35 | | #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> |
36 | | #include <comphelper/diagnose_ex.hxx> |
37 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
38 | | #include <basegfx/polygon/b2dpolygon.hxx> |
39 | | #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> |
40 | | #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> |
41 | | #include <basegfx/matrix/b2dhommatrixtools.hxx> |
42 | | #include <officecfg/Office/Common.hxx> |
43 | | |
44 | | using namespace com::sun::star; |
45 | | |
46 | | namespace drawinglayer::primitive2d |
47 | | { |
48 | | void ControlPrimitive2D::createXControl() |
49 | 0 | { |
50 | 0 | if(mxXControl.is() || !getControlModel().is()) |
51 | 0 | return; |
52 | | |
53 | 0 | uno::Reference< beans::XPropertySet > xSet(getControlModel(), uno::UNO_QUERY); |
54 | |
|
55 | 0 | if(!xSet.is()) |
56 | 0 | return; |
57 | | |
58 | 0 | uno::Any aValue(xSet->getPropertyValue(u"DefaultControl"_ustr)); |
59 | 0 | OUString aUnoControlTypeName; |
60 | |
|
61 | 0 | if(!(aValue >>= aUnoControlTypeName)) |
62 | 0 | return; |
63 | | |
64 | 0 | if(aUnoControlTypeName.isEmpty()) |
65 | 0 | return; |
66 | | |
67 | 0 | const uno::Reference< uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() ); |
68 | 0 | uno::Reference< awt::XControl > xXControl( |
69 | 0 | xContext->getServiceManager()->createInstanceWithContext(aUnoControlTypeName, xContext), uno::UNO_QUERY); |
70 | |
|
71 | 0 | if(xXControl.is()) |
72 | 0 | { |
73 | 0 | xXControl->setModel(getControlModel()); |
74 | | |
75 | | // remember XControl |
76 | 0 | mxXControl = std::move(xXControl); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | Primitive2DReference ControlPrimitive2D::createBitmapDecomposition(const geometry::ViewInformation2D& rViewInformation) const |
81 | 0 | { |
82 | 0 | Primitive2DReference xRetval; |
83 | 0 | const uno::Reference< awt::XControl >& rXControl(getXControl()); |
84 | |
|
85 | 0 | if(rXControl.is()) |
86 | 0 | { |
87 | 0 | uno::Reference< awt::XWindow > xControlWindow(rXControl, uno::UNO_QUERY); |
88 | |
|
89 | 0 | if(xControlWindow.is()) |
90 | 0 | { |
91 | | // get decomposition to get size |
92 | 0 | basegfx::B2DVector aScale, aTranslate; |
93 | 0 | double fRotate, fShearX; |
94 | 0 | getTransform().decompose(aScale, aTranslate, fRotate, fShearX); |
95 | | |
96 | | // get absolute discrete size (no mirror or rotate here) |
97 | 0 | aScale = basegfx::absolute(aScale); |
98 | 0 | basegfx::B2DVector aDiscreteSize(rViewInformation.getObjectToViewTransformation() * aScale); |
99 | | |
100 | | // limit to a maximum square size, e.g. 300x150 pixels (45000) |
101 | 0 | const double fDiscreteMax(officecfg::Office::Common::Drawinglayer::QuadraticFormControlRenderLimit::get()); |
102 | 0 | const double fDiscreteQuadratic(aDiscreteSize.getX() * aDiscreteSize.getY()); |
103 | 0 | const bool bScaleUsed(fDiscreteQuadratic > fDiscreteMax); |
104 | 0 | double fFactor(1.0); |
105 | |
|
106 | 0 | if(bScaleUsed) |
107 | 0 | { |
108 | | // get factor and adapt to scaled size |
109 | 0 | fFactor = sqrt(fDiscreteMax / fDiscreteQuadratic); |
110 | 0 | aDiscreteSize *= fFactor; |
111 | 0 | } |
112 | | |
113 | | // go to integer |
114 | 0 | const sal_Int32 nSizeX(basegfx::fround(aDiscreteSize.getX())); |
115 | 0 | const sal_Int32 nSizeY(basegfx::fround(aDiscreteSize.getY())); |
116 | |
|
117 | 0 | if(nSizeX > 0 && nSizeY > 0) |
118 | 0 | { |
119 | | // prepare VirtualDevice |
120 | 0 | ScopedVclPtrInstance< VirtualDevice > aVirtualDevice(*Application::GetDefaultDevice()); |
121 | 0 | const Size aSizePixel(nSizeX, nSizeY); |
122 | 0 | aVirtualDevice->SetOutputSizePixel(aSizePixel); |
123 | | |
124 | | // set size at control |
125 | 0 | xControlWindow->setPosSize(0, 0, nSizeX, nSizeY, awt::PosSize::POSSIZE); |
126 | | |
127 | | // get graphics and view |
128 | 0 | uno::Reference< awt::XGraphics > xGraphics(aVirtualDevice->CreateUnoGraphics()); |
129 | 0 | uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY); |
130 | |
|
131 | 0 | if(xGraphics.is() && xControlView.is()) |
132 | 0 | { |
133 | | // link graphics and view |
134 | 0 | xControlView->setGraphics(xGraphics); |
135 | |
|
136 | 0 | { // #i93162# For painting the control setting a Zoom (using setZoom() at the xControlView) |
137 | | // is needed to define the font size. Normally this is done in |
138 | | // ViewObjectContactOfUnoControl::createPrimitive2DSequence by using positionControlForPaint(). |
139 | | // For some reason the difference between MapUnit::MapTwipS and MapUnit::Map100thMM still plays |
140 | | // a role there so that for Draw/Impress/Calc (the MapUnit::Map100thMM users) i need to set a zoom |
141 | | // here, too. The factor includes the needed scale, but is calculated by pure comparisons. It |
142 | | // is somehow related to the twips/100thmm relationship. |
143 | 0 | bool bUserIs100thmm(false); |
144 | 0 | const uno::Reference< awt::XControl > xControl(xControlView, uno::UNO_QUERY); |
145 | |
|
146 | 0 | if(xControl.is()) |
147 | 0 | { |
148 | 0 | uno::Reference<awt::XWindowPeer> xWindowPeer(xControl->getPeer()); |
149 | 0 | if (xWindowPeer) |
150 | 0 | { |
151 | 0 | uno::Reference<awt::XVclWindowPeer> xPeerProps(xWindowPeer, uno::UNO_QUERY_THROW); |
152 | 0 | uno::Any aAny = xPeerProps->getProperty(u"ParentIs100thmm"_ustr); // see VCLXWindow::getProperty |
153 | 0 | aAny >>= bUserIs100thmm; |
154 | 0 | } |
155 | 0 | } |
156 | |
|
157 | 0 | if(bUserIs100thmm) |
158 | 0 | { |
159 | | // calc screen zoom for text display. fFactor is already added indirectly in aDiscreteSize |
160 | 0 | basegfx::B2DVector aScreenZoom( |
161 | 0 | basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : aDiscreteSize.getX() / aScale.getX(), |
162 | 0 | basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : aDiscreteSize.getY() / aScale.getY()); |
163 | 0 | static const double fZoomScale(28.0); // do not ask for this constant factor, but it gets the zoom right |
164 | 0 | aScreenZoom *= fZoomScale; |
165 | | |
166 | | // set zoom at control view for text scaling |
167 | 0 | xControlView->setZoom(static_cast<float>(aScreenZoom.getX()), static_cast<float>(aScreenZoom.getY())); |
168 | 0 | } |
169 | 0 | } |
170 | |
|
171 | 0 | try |
172 | 0 | { |
173 | | // try to paint it to VirtualDevice |
174 | 0 | xControlView->draw(0, 0); |
175 | | |
176 | | // get bitmap |
177 | 0 | const Bitmap aContent(aVirtualDevice->GetBitmap(Point(), aSizePixel)); |
178 | | |
179 | | // snap translate and scale to discrete position (pixel) to avoid sub-pixel offset and blurring further |
180 | 0 | basegfx::B2DVector aSnappedTranslate(basegfx::fround(rViewInformation.getObjectToViewTransformation() * aTranslate)); |
181 | 0 | aSnappedTranslate = rViewInformation.getInverseObjectToViewTransformation() * aSnappedTranslate; |
182 | 0 | basegfx::B2DVector aSnappedScale(basegfx::fround(rViewInformation.getObjectToViewTransformation() * aScale)); |
183 | 0 | aSnappedScale = rViewInformation.getInverseObjectToViewTransformation() * aSnappedScale; |
184 | | |
185 | | // short form for scale and translate transformation |
186 | 0 | const basegfx::B2DHomMatrix aBitmapTransform(basegfx::utils::createScaleTranslateB2DHomMatrix( |
187 | 0 | aSnappedScale.getX(), aSnappedScale.getY(), aSnappedTranslate.getX(), aSnappedTranslate.getY())); |
188 | | |
189 | | // create primitive |
190 | 0 | xRetval = new BitmapPrimitive2D( |
191 | 0 | aContent, |
192 | 0 | aBitmapTransform); |
193 | 0 | } |
194 | 0 | catch( const uno::Exception& ) |
195 | 0 | { |
196 | 0 | DBG_UNHANDLED_EXCEPTION("drawinglayer"); |
197 | 0 | } |
198 | 0 | } |
199 | 0 | } |
200 | 0 | } |
201 | 0 | } |
202 | |
|
203 | 0 | return xRetval; |
204 | 0 | } |
205 | | |
206 | | Primitive2DReference ControlPrimitive2D::createPlaceholderDecomposition() const |
207 | 0 | { |
208 | | // create a gray placeholder hairline polygon in object size |
209 | 0 | basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0); |
210 | 0 | aObjectRange.transform(getTransform()); |
211 | 0 | basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aObjectRange)); |
212 | 0 | const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0); |
213 | | |
214 | | // The replacement object may also get a text like 'empty group' here later |
215 | 0 | Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(std::move(aOutline), aGrayTone)); |
216 | |
|
217 | 0 | return xRetval; |
218 | 0 | } |
219 | | |
220 | | Primitive2DReference ControlPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const |
221 | 0 | { |
222 | | // try to create a bitmap decomposition. If that fails for some reason, |
223 | | // at least create a replacement decomposition. |
224 | 0 | Primitive2DReference xReference(createBitmapDecomposition(rViewInformation)); |
225 | |
|
226 | 0 | if(!xReference.is()) |
227 | 0 | { |
228 | 0 | xReference = createPlaceholderDecomposition(); |
229 | 0 | } |
230 | |
|
231 | 0 | return xReference; |
232 | 0 | } |
233 | | |
234 | | ControlPrimitive2D::ControlPrimitive2D( |
235 | | basegfx::B2DHomMatrix aTransform, |
236 | | uno::Reference< awt::XControlModel > xControlModel, |
237 | | uno::Reference<awt::XControl> xXControl, |
238 | | ::std::u16string_view const rTitle, |
239 | | ::std::u16string_view const rDescription, |
240 | | void const*const pAnchorKey) |
241 | 0 | : maTransform(std::move(aTransform)), |
242 | 0 | mxControlModel(std::move(xControlModel)), |
243 | 0 | mxXControl(std::move(xXControl)) |
244 | 0 | , m_pAnchorStructureElementKey(pAnchorKey) |
245 | 0 | { |
246 | 0 | ::rtl::OUStringBuffer buf(rTitle); |
247 | 0 | if (!rTitle.empty() && !rDescription.empty()) |
248 | 0 | { |
249 | 0 | buf.append(" - "); |
250 | 0 | } |
251 | 0 | buf.append(rDescription); |
252 | 0 | m_AltText = buf.makeStringAndClear(); |
253 | 0 | } |
254 | | |
255 | | const uno::Reference< awt::XControl >& ControlPrimitive2D::getXControl() const |
256 | 0 | { |
257 | 0 | if(!mxXControl.is()) |
258 | 0 | { |
259 | 0 | const_cast< ControlPrimitive2D* >(this)->createXControl(); |
260 | 0 | } |
261 | |
|
262 | 0 | return mxXControl; |
263 | 0 | } |
264 | | |
265 | | bool ControlPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const |
266 | 0 | { |
267 | | // use base class compare operator |
268 | 0 | if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive)) |
269 | 0 | return false; |
270 | | |
271 | 0 | const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive); |
272 | |
|
273 | 0 | if(getTransform() != rCompare.getTransform()) |
274 | 0 | return false; |
275 | | |
276 | | // check if ControlModel references both are/are not |
277 | 0 | if (getControlModel().is() != rCompare.getControlModel().is()) |
278 | 0 | return false; |
279 | | |
280 | 0 | if(getControlModel().is()) |
281 | 0 | { |
282 | | // both exist, check for equality |
283 | 0 | if (getControlModel() != rCompare.getControlModel()) |
284 | 0 | return false; |
285 | 0 | } |
286 | | |
287 | | // check if XControl references both are/are not |
288 | 0 | if (getXControl().is() != rCompare.getXControl().is()) |
289 | 0 | return false; |
290 | | |
291 | 0 | if(getXControl().is()) |
292 | 0 | { |
293 | | // both exist, check for equality |
294 | 0 | if (getXControl() != rCompare.getXControl()) |
295 | 0 | return false; |
296 | 0 | } |
297 | | |
298 | 0 | return true; |
299 | 0 | } |
300 | | |
301 | | basegfx::B2DRange ControlPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const |
302 | 0 | { |
303 | | // simply derivate from unit range |
304 | 0 | basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0); |
305 | 0 | aRetval.transform(getTransform()); |
306 | 0 | return aRetval; |
307 | 0 | } |
308 | | |
309 | | bool ControlPrimitive2D::isVisibleAsChildWindow() const |
310 | 0 | { |
311 | | // find out if the control is already visualized as a VCL-ChildWindow |
312 | 0 | const uno::Reference<awt::XControl>& rXControl(getXControl()); |
313 | |
|
314 | 0 | try |
315 | 0 | { |
316 | 0 | uno::Reference<awt::XWindow2> xControlWindow(rXControl, uno::UNO_QUERY_THROW); |
317 | 0 | return rXControl->getPeer().is() && xControlWindow->isVisible(); |
318 | 0 | } |
319 | 0 | catch (const uno::Exception&) |
320 | 0 | { |
321 | | // #i116763# since there is a good alternative when the xControlView |
322 | | // is not found and it is allowed to happen |
323 | 0 | } |
324 | | |
325 | 0 | return false; |
326 | 0 | } |
327 | | |
328 | | void ControlPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const |
329 | 0 | { |
330 | | // this primitive is view-dependent related to the scaling. If scaling has changed, |
331 | | // destroy existing decomposition. To detect change, use size of unit size in view coordinates |
332 | 0 | const basegfx::B2DVector aNewScaling(rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); |
333 | |
|
334 | 0 | if(hasBuffered2DDecomposition()) |
335 | 0 | { |
336 | 0 | if(!maLastViewScaling.equal(aNewScaling)) |
337 | 0 | { |
338 | | // conditions of last local decomposition have changed, delete |
339 | 0 | const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); |
340 | 0 | } |
341 | 0 | } |
342 | |
|
343 | 0 | if(!hasBuffered2DDecomposition()) |
344 | 0 | { |
345 | | // remember ViewTransformation |
346 | 0 | const_cast< ControlPrimitive2D* >(this)->maLastViewScaling = aNewScaling; |
347 | 0 | } |
348 | | |
349 | | // use parent implementation |
350 | 0 | BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); |
351 | 0 | } |
352 | | |
353 | | // provide unique ID |
354 | | sal_uInt32 ControlPrimitive2D::getPrimitive2DID() const |
355 | 0 | { |
356 | 0 | return PRIMITIVE2D_ID_CONTROLPRIMITIVE2D; |
357 | 0 | } |
358 | | |
359 | | } // end of namespace |
360 | | |
361 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |