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