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