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