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