/src/libreoffice/svx/source/sdr/contact/viewobjectcontactofunocontrol.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 | | |
21 | | #include <sdr/contact/viewobjectcontactofunocontrol.hxx> |
22 | | #include <sdr/contact/viewcontactofunocontrol.hxx> |
23 | | #include <svx/sdr/contact/displayinfo.hxx> |
24 | | #include <svx/sdr/contact/objectcontactofpageview.hxx> |
25 | | #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> |
26 | | #include <svx/svdouno.hxx> |
27 | | #include <svx/svdpagv.hxx> |
28 | | #include <svx/svdview.hxx> |
29 | | #include <svx/sdrpagewindow.hxx> |
30 | | |
31 | | #include <com/sun/star/awt/XControl.hpp> |
32 | | #include <com/sun/star/awt/XControlContainer.hpp> |
33 | | #include <com/sun/star/awt/XWindow2.hpp> |
34 | | #include <com/sun/star/awt/PosSize.hpp> |
35 | | #include <com/sun/star/awt/XView.hpp> |
36 | | #include <com/sun/star/beans/XPropertySet.hpp> |
37 | | #include <com/sun/star/beans/XPropertySetInfo.hpp> |
38 | | #include <com/sun/star/lang/XComponent.hpp> |
39 | | #include <com/sun/star/awt/InvalidateStyle.hpp> |
40 | | #include <com/sun/star/util/XModeChangeListener.hpp> |
41 | | #include <com/sun/star/util/XModeChangeBroadcaster.hpp> |
42 | | #include <com/sun/star/uno/XComponentContext.hpp> |
43 | | #include <com/sun/star/container/XContainerListener.hpp> |
44 | | #include <com/sun/star/container/XContainer.hpp> |
45 | | |
46 | | #include <vcl/canvastools.hxx> |
47 | | #include <vcl/svapp.hxx> |
48 | | #include <vcl/unohelp.hxx> |
49 | | #include <vcl/window.hxx> |
50 | | #include <comphelper/lok.hxx> |
51 | | #include <comphelper/processfactory.hxx> |
52 | | #include <comphelper/flagguard.hxx> |
53 | | #include <cppuhelper/implbase.hxx> |
54 | | #include <toolkit/controls/unocontrolcontainer.hxx> |
55 | | #include <toolkit/helper/vclunohelper.hxx> |
56 | | #include <comphelper/diagnose_ex.hxx> |
57 | | #include <tools/debug.hxx> |
58 | | #include <basegfx/matrix/b2dhommatrix.hxx> |
59 | | #include <basegfx/matrix/b2dhommatrixtools.hxx> |
60 | | #include <drawinglayer/primitive2d/controlprimitive2d.hxx> |
61 | | #include <drawinglayer/primitive2d/groupprimitive2d.hxx> |
62 | | |
63 | | #include <utility> |
64 | | /* |
65 | | |
66 | | Form controls (more precise: UNO Controls) in the drawing layer are ... prone to breakage, since they have some |
67 | | specialities which the drawing layer currently doesn't capture too well. In particular, having a living VCL |
68 | | window as child of the document window, and coupling this Window to a drawing layer object, makes things |
69 | | difficult sometimes. |
70 | | |
71 | | Below is a list of issues which existed in the past. Whenever you change code here, you're encouraged to |
72 | | verify those issues are still fixed. (Whenever you have some additional time, you're encouraged to write |
73 | | an automatic test for one or more of those issues for which this is possible :) |
74 | | |
75 | | https://bz.apache.org/ooo/show_bug.cgi?id=105992 |
76 | | zooming documents containing (alive) form controls improperly positions the controls |
77 | | |
78 | | https://bz.apache.org/ooo/show_bug.cgi?id=104362 |
79 | | crash when copy a control |
80 | | |
81 | | https://bz.apache.org/ooo/show_bug.cgi?id=104544 |
82 | | Gridcontrol duplicated after design view on/off |
83 | | |
84 | | https://bz.apache.org/ooo/show_bug.cgi?id=102089 |
85 | | print preview shows control elements with property printable=false |
86 | | |
87 | | https://bz.apache.org/ooo/show_bug.cgi?id=102090 |
88 | | problem with setVisible on TextControl |
89 | | |
90 | | https://bz.apache.org/ooo/show_bug.cgi?id=103138 |
91 | | loop when insert a control in draw |
92 | | |
93 | | https://bz.apache.org/ooo/show_bug.cgi?id=101398 |
94 | | initially-displaying a document with many controls is very slow |
95 | | |
96 | | https://bz.apache.org/ooo/show_bug.cgi?id=72429 |
97 | | repaint error in form wizard in bugdoc database |
98 | | |
99 | | https://bz.apache.org/ooo/show_bug.cgi?id=72694 |
100 | | form control artifacts when scrolling a text fast |
101 | | |
102 | | */ |
103 | | |
104 | | |
105 | | namespace sdr::contact { |
106 | | |
107 | | |
108 | | using namespace ::com::sun::star::awt::InvalidateStyle; |
109 | | using ::com::sun::star::uno::Reference; |
110 | | using ::com::sun::star::uno::XInterface; |
111 | | using ::com::sun::star::uno::UNO_QUERY; |
112 | | using ::com::sun::star::uno::UNO_QUERY_THROW; |
113 | | using ::com::sun::star::uno::Exception; |
114 | | using ::com::sun::star::awt::XControl; |
115 | | using ::com::sun::star::awt::XControlModel; |
116 | | using ::com::sun::star::awt::XControlContainer; |
117 | | using ::com::sun::star::awt::XWindow2; |
118 | | using ::com::sun::star::awt::XWindowListener; |
119 | | using ::com::sun::star::awt::PosSize::POSSIZE; |
120 | | using ::com::sun::star::awt::XView; |
121 | | using ::com::sun::star::awt::WindowEvent; |
122 | | using ::com::sun::star::beans::XPropertySet; |
123 | | using ::com::sun::star::beans::XPropertySetInfo; |
124 | | using ::com::sun::star::lang::XComponent; |
125 | | using ::com::sun::star::awt::XWindowPeer; |
126 | | using ::com::sun::star::beans::XPropertyChangeListener; |
127 | | using ::com::sun::star::util::XModeChangeListener; |
128 | | using ::com::sun::star::util::XModeChangeBroadcaster; |
129 | | using ::com::sun::star::util::ModeChangeEvent; |
130 | | using ::com::sun::star::lang::EventObject; |
131 | | using ::com::sun::star::beans::PropertyChangeEvent; |
132 | | using ::com::sun::star::container::XContainerListener; |
133 | | using ::com::sun::star::container::XContainer; |
134 | | using ::com::sun::star::container::ContainerEvent; |
135 | | using ::com::sun::star::uno::Any; |
136 | | |
137 | | namespace { |
138 | | |
139 | | class ControlHolder |
140 | | { |
141 | | private: |
142 | | Reference< XControl > m_xControl; |
143 | | Reference< XWindow2 > m_xControlWindow; |
144 | | Reference< XView > m_xControlView; |
145 | | |
146 | | public: |
147 | | ControlHolder() |
148 | 0 | { |
149 | 0 | } |
150 | | |
151 | | explicit ControlHolder( const Reference< XControl >& _rxControl ) |
152 | 0 | { |
153 | 0 | *this = _rxControl; |
154 | 0 | } |
155 | | |
156 | | ControlHolder& operator=( const Reference< XControl >& _rxControl ) |
157 | 0 | { |
158 | 0 | clear(); |
159 | |
|
160 | 0 | m_xControl = _rxControl; |
161 | 0 | if ( m_xControl.is() ) |
162 | 0 | { |
163 | 0 | m_xControlWindow.set( m_xControl, UNO_QUERY ); |
164 | 0 | m_xControlView.set( m_xControl, UNO_QUERY ); |
165 | 0 | if ( !m_xControlWindow.is() || !m_xControlView.is() ) |
166 | 0 | { |
167 | 0 | OSL_FAIL( "ControlHolder::operator=: invalid XControl, missing required interfaces!" ); |
168 | 0 | clear(); |
169 | 0 | } |
170 | 0 | } |
171 | |
|
172 | 0 | return *this; |
173 | 0 | } |
174 | | |
175 | | public: |
176 | 0 | bool is() const { return m_xControl.is() && m_xControlWindow.is() && m_xControlView.is(); } |
177 | 0 | void clear() { m_xControl.clear(); m_xControlWindow.clear(); m_xControlView.clear(); } |
178 | | |
179 | | // delegators for the methods of the UNO interfaces |
180 | | // Note all those will crash if called for a NULL object. |
181 | 0 | bool isDesignMode() const { return m_xControl->isDesignMode(); } |
182 | 0 | void setDesignMode( const bool _bDesign ) const { m_xControl->setDesignMode( _bDesign ); } |
183 | 0 | bool isVisible() const { return m_xControlWindow->isVisible(); } |
184 | 0 | void setVisible( const bool _bVisible ) const { m_xControlWindow->setVisible( _bVisible ); } |
185 | | Reference< XControlModel > |
186 | 0 | getModel() const { return m_xControl->getModel(); } |
187 | 0 | void setModel( const Reference< XControlModel >& _m ) const { m_xControl->setModel( _m ); } |
188 | | |
189 | 0 | void addWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->addWindowListener( _l ); } |
190 | 0 | void removeWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->removeWindowListener( _l ); } |
191 | | void setPosSize( const tools::Rectangle& _rPosSize ) const; |
192 | | tools::Rectangle |
193 | | getPosSize() const; |
194 | | void setZoom( const ::basegfx::B2DVector& _rScale ) const; |
195 | | ::basegfx::B2DVector |
196 | | getZoom() const; |
197 | | |
198 | | void invalidate() const; |
199 | | |
200 | | public: |
201 | 0 | const Reference< XControl >& getControl() const { return m_xControl; } |
202 | | }; |
203 | | |
204 | | bool operator==( const ControlHolder& _rControl, const Reference< XInterface >& _rxCompare ) |
205 | 0 | { |
206 | 0 | return _rControl.getControl() == _rxCompare; |
207 | 0 | } |
208 | | |
209 | | bool operator==( const ControlHolder& _rControl, const Any& _rxCompare ) |
210 | 0 | { |
211 | 0 | return _rControl == Reference< XInterface >( _rxCompare, UNO_QUERY ); |
212 | 0 | } |
213 | | |
214 | | } |
215 | | |
216 | | void ControlHolder::setPosSize( const tools::Rectangle& _rPosSize ) const |
217 | 0 | { |
218 | | // no check whether we're valid, this is the responsibility of the caller |
219 | | |
220 | | // don't call setPosSize when pos/size did not change #i104181# |
221 | 0 | ::tools::Rectangle aCurrentRect( getPosSize() ); |
222 | 0 | if ( aCurrentRect != _rPosSize ) |
223 | 0 | { |
224 | 0 | m_xControlWindow->setPosSize( |
225 | 0 | _rPosSize.Left(), _rPosSize.Top(), _rPosSize.GetWidth(), _rPosSize.GetHeight(), |
226 | 0 | POSSIZE |
227 | 0 | ); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | |
232 | | ::tools::Rectangle ControlHolder::getPosSize() const |
233 | 0 | { |
234 | | // no check whether we're valid, this is the responsibility of the caller |
235 | 0 | return vcl::unohelper::ConvertToVCLRect( m_xControlWindow->getPosSize() ); |
236 | 0 | } |
237 | | |
238 | | |
239 | | void ControlHolder::setZoom( const ::basegfx::B2DVector& _rScale ) const |
240 | 0 | { |
241 | | // no check whether we're valid, this is the responsibility of the caller |
242 | 0 | m_xControlView->setZoom( static_cast<float>(_rScale.getX()), static_cast<float>(_rScale.getY()) ); |
243 | 0 | } |
244 | | |
245 | | |
246 | | void ControlHolder::invalidate() const |
247 | 0 | { |
248 | 0 | Reference< XWindowPeer > xPeer( m_xControl->getPeer() ); |
249 | 0 | if ( xPeer.is() ) |
250 | 0 | { |
251 | 0 | VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xPeer ); |
252 | 0 | OSL_ENSURE( pWindow, "ControlHolder::invalidate: no implementation access!" ); |
253 | 0 | if ( pWindow ) |
254 | 0 | pWindow->Invalidate(); |
255 | 0 | } |
256 | 0 | } |
257 | | |
258 | | |
259 | | ::basegfx::B2DVector ControlHolder::getZoom() const |
260 | 0 | { |
261 | | // no check whether we're valid, this is the responsibility of the caller |
262 | | |
263 | | // Argh. Why does XView have a setZoom only, but not a getZoom? |
264 | 0 | VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( m_xControl->getPeer() ); |
265 | 0 | OSL_ENSURE( pWindow, "ControlHolder::getZoom: no implementation access!" ); |
266 | |
|
267 | 0 | ::basegfx::B2DVector aZoom( 1, 1 ); |
268 | 0 | if ( pWindow ) |
269 | 0 | { |
270 | 0 | const Fraction& rZoom( pWindow->GetZoom() ); |
271 | 0 | aZoom.setX( static_cast<double>(rZoom) ); |
272 | 0 | aZoom.setY( static_cast<double>(rZoom) ); |
273 | 0 | } |
274 | 0 | return aZoom; |
275 | 0 | } |
276 | | |
277 | | namespace UnoControlContactHelper { |
278 | | |
279 | | /** positions a control, and sets its zoom mode, using a given transformation and output device |
280 | | */ |
281 | | static void adjustControlGeometry_throw( const ControlHolder& _rControl, const tools::Rectangle& _rLogicBoundingRect, |
282 | | const basegfx::B2DHomMatrix& _rViewTransformation, const ::basegfx::B2DHomMatrix& _rZoomLevelNormalization ) |
283 | 0 | { |
284 | | // In the LOK case, control geometry is handled by LokControlHandler |
285 | | // except when the document is exported to PDF or printed, |
286 | | // so we use isTiledPainting() in place of the more generic isActive() |
287 | 0 | if (comphelper::LibreOfficeKit::isTiledPainting()) |
288 | 0 | return; |
289 | | |
290 | 0 | OSL_PRECOND( _rControl.is(), "UnoControlContactHelper::adjustControlGeometry_throw: illegal control!" ); |
291 | 0 | if ( !_rControl.is() ) |
292 | 0 | return; |
293 | | |
294 | | #if OSL_DEBUG_LEVEL > 0 |
295 | | ::basegfx::B2DTuple aViewScale, aViewTranslate; |
296 | | double nViewRotate(0), nViewShearX(0); |
297 | | _rViewTransformation.decompose( aViewScale, aViewTranslate, nViewRotate, nViewShearX ); |
298 | | |
299 | | ::basegfx::B2DTuple aZoomScale, aZoomTranslate; |
300 | | double nZoomRotate(0), nZoomShearX(0); |
301 | | _rZoomLevelNormalization.decompose( aZoomScale, aZoomTranslate, nZoomRotate, nZoomShearX ); |
302 | | #endif |
303 | | |
304 | | // transform the logic bound rect, using the view transformation, to pixel coordinates |
305 | 0 | ::basegfx::B2DPoint aTopLeft( _rLogicBoundingRect.Left(), _rLogicBoundingRect.Top() ); |
306 | 0 | aTopLeft *= _rViewTransformation; |
307 | 0 | ::basegfx::B2DPoint aBottomRight( _rLogicBoundingRect.Right(), _rLogicBoundingRect.Bottom() ); |
308 | 0 | aBottomRight *= _rViewTransformation; |
309 | |
|
310 | 0 | const tools::Rectangle aPaintRectPixel(static_cast<tools::Long>(std::round(aTopLeft.getX())), |
311 | 0 | static_cast<tools::Long>(std::round(aTopLeft.getY())), |
312 | 0 | static_cast<tools::Long>(std::round(aBottomRight.getX())), |
313 | 0 | static_cast<tools::Long>(std::round(aBottomRight.getY()))); |
314 | 0 | _rControl.setPosSize( aPaintRectPixel ); |
315 | | |
316 | | // determine the scale from the current view transformation, and the normalization matrix |
317 | 0 | ::basegfx::B2DHomMatrix aObtainResolutionDependentScale( _rViewTransformation * _rZoomLevelNormalization ); |
318 | 0 | ::basegfx::B2DVector aScale, aTranslate; |
319 | 0 | double fRotate, fShearX; |
320 | 0 | aObtainResolutionDependentScale.decompose( aScale, aTranslate, fRotate, fShearX ); |
321 | 0 | _rControl.setZoom( aScale ); |
322 | 0 | } |
323 | | |
324 | | /** disposes the given control |
325 | | */ |
326 | | static void disposeAndClearControl_nothrow( ControlHolder& _rControl ) |
327 | 0 | { |
328 | 0 | try |
329 | 0 | { |
330 | 0 | Reference< XComponent > xControlComp = _rControl.getControl(); |
331 | 0 | if ( xControlComp.is() ) |
332 | 0 | xControlComp->dispose(); |
333 | 0 | } |
334 | 0 | catch( const Exception& ) |
335 | 0 | { |
336 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
337 | 0 | } |
338 | 0 | _rControl.clear(); |
339 | 0 | } |
340 | | |
341 | | } |
342 | | |
343 | | namespace { |
344 | | |
345 | | /** interface encapsulating access to an SdrPageView, stripped down to the methods we really need |
346 | | */ |
347 | | class IPageViewAccess |
348 | | { |
349 | | public: |
350 | | /** determines whether the view is currently in design mode |
351 | | */ |
352 | | virtual bool isDesignMode() const = 0; |
353 | | |
354 | | /** retrieves the control container for a given output device |
355 | | */ |
356 | | virtual Reference< XControlContainer > |
357 | | getControlContainer( const OutputDevice& _rDevice ) const = 0; |
358 | | |
359 | | /** determines whether a given layer is visible |
360 | | */ |
361 | | virtual bool isLayerVisible( SdrLayerID _nLayerID ) const = 0; |
362 | | |
363 | | protected: |
364 | 0 | ~IPageViewAccess() {} |
365 | | }; |
366 | | |
367 | | /** is a ->IPageViewAccess implementation based on a real ->SdrPageView instance |
368 | | */ |
369 | | class SdrPageViewAccess : public IPageViewAccess |
370 | | { |
371 | | const SdrPageView& m_rPageView; |
372 | | public: |
373 | 0 | explicit SdrPageViewAccess( const SdrPageView& _rPageView ) : m_rPageView( _rPageView ) { } |
374 | | |
375 | 0 | virtual ~SdrPageViewAccess() {} |
376 | | |
377 | | virtual bool isDesignMode() const override; |
378 | | virtual Reference< XControlContainer > |
379 | | getControlContainer( const OutputDevice& _rDevice ) const override; |
380 | | virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override; |
381 | | }; |
382 | | |
383 | | } |
384 | | |
385 | | bool SdrPageViewAccess::isDesignMode() const |
386 | 0 | { |
387 | 0 | return m_rPageView.GetView().IsDesignMode(); |
388 | 0 | } |
389 | | |
390 | | |
391 | | Reference< XControlContainer > SdrPageViewAccess::getControlContainer( const OutputDevice& _rDevice ) const |
392 | 0 | { |
393 | 0 | Reference< XControlContainer > xControlContainer = m_rPageView.GetControlContainer( _rDevice ); |
394 | 0 | DBG_ASSERT( xControlContainer.is() || nullptr == m_rPageView.FindPageWindow( _rDevice ), |
395 | 0 | "SdrPageViewAccess::getControlContainer: the output device is known, but there is no control container for it?" ); |
396 | 0 | return xControlContainer; |
397 | 0 | } |
398 | | |
399 | | |
400 | | bool SdrPageViewAccess::isLayerVisible( SdrLayerID _nLayerID ) const |
401 | 0 | { |
402 | 0 | return m_rPageView.GetVisibleLayers().IsSet( _nLayerID ); |
403 | 0 | } |
404 | | |
405 | | namespace { |
406 | | |
407 | | /** is a ->IPageViewAccess implementation which can be used to create an invisible control for |
408 | | an arbitrary window |
409 | | */ |
410 | | class InvisibleControlViewAccess : public IPageViewAccess |
411 | | { |
412 | | private: |
413 | | Reference< XControlContainer >& m_rControlContainer; |
414 | | public: |
415 | | explicit InvisibleControlViewAccess( Reference< XControlContainer >& _inout_ControlContainer ) |
416 | 0 | :m_rControlContainer( _inout_ControlContainer ) |
417 | 0 | { |
418 | 0 | } |
419 | | |
420 | 0 | virtual ~InvisibleControlViewAccess() {} |
421 | | |
422 | | virtual bool isDesignMode() const override; |
423 | | virtual Reference< XControlContainer > |
424 | | getControlContainer( const OutputDevice& _rDevice ) const override; |
425 | | virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override; |
426 | | }; |
427 | | |
428 | | } |
429 | | |
430 | | bool InvisibleControlViewAccess::isDesignMode() const |
431 | 0 | { |
432 | 0 | return true; |
433 | 0 | } |
434 | | |
435 | | |
436 | | Reference< XControlContainer > InvisibleControlViewAccess::getControlContainer( const OutputDevice& _rDevice ) const |
437 | 0 | { |
438 | 0 | if ( !m_rControlContainer.is() ) |
439 | 0 | { |
440 | 0 | const vcl::Window* pWindow = _rDevice.GetOwnerWindow(); |
441 | 0 | OSL_ENSURE( pWindow, "InvisibleControlViewAccess::getControlContainer: expected to be called for a window only!" ); |
442 | 0 | if ( pWindow ) |
443 | 0 | m_rControlContainer = VCLUnoHelper::CreateControlContainer( const_cast< vcl::Window* >( pWindow ) ); |
444 | 0 | } |
445 | 0 | return m_rControlContainer; |
446 | 0 | } |
447 | | |
448 | | |
449 | | bool InvisibleControlViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const |
450 | 0 | { |
451 | 0 | return false; |
452 | 0 | } |
453 | | |
454 | | namespace { |
455 | | |
456 | | //= DummyPageViewAccess |
457 | | |
458 | | /** is a ->IPageViewAccess implementation which can be used to create a control for an arbitrary |
459 | | non-Window device |
460 | | |
461 | | The implementation will report the "PageView" as being in design mode, all layers to be visible, |
462 | | and will not return any ControlContainer, so all control container related features (notifications etc) |
463 | | are not available. |
464 | | */ |
465 | | class DummyPageViewAccess : public IPageViewAccess |
466 | | { |
467 | | public: |
468 | | DummyPageViewAccess() |
469 | 0 | { |
470 | 0 | } |
471 | | |
472 | 0 | virtual ~DummyPageViewAccess() {} |
473 | | |
474 | | virtual bool isDesignMode() const override; |
475 | | virtual Reference< XControlContainer > |
476 | | getControlContainer( const OutputDevice& _rDevice ) const override; |
477 | | virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override; |
478 | | }; |
479 | | |
480 | | } |
481 | | |
482 | | bool DummyPageViewAccess::isDesignMode() const |
483 | 0 | { |
484 | 0 | return true; |
485 | 0 | } |
486 | | |
487 | | |
488 | | Reference< XControlContainer > DummyPageViewAccess::getControlContainer( const OutputDevice& /*_rDevice*/ ) const |
489 | 0 | { |
490 | 0 | return nullptr; |
491 | 0 | } |
492 | | |
493 | | |
494 | | bool DummyPageViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const |
495 | 0 | { |
496 | 0 | return true; |
497 | 0 | } |
498 | | |
499 | | |
500 | | //= ViewObjectContactOfUnoControl_Impl |
501 | | |
502 | | typedef ::cppu::WeakImplHelper < XWindowListener |
503 | | , XPropertyChangeListener |
504 | | , XContainerListener |
505 | | , XModeChangeListener |
506 | | > ViewObjectContactOfUnoControl_Impl_Base; |
507 | | |
508 | | class ViewObjectContactOfUnoControl_Impl: |
509 | | public ViewObjectContactOfUnoControl_Impl_Base |
510 | | { |
511 | | private: |
512 | | // tdf#41935 note that access to members is protected with SolarMutex; |
513 | | // the class previously had its own mutex but that is prone to deadlock |
514 | | |
515 | | /// the instance whose IMPL we are |
516 | | ViewObjectContactOfUnoControl* m_pAntiImpl; |
517 | | |
518 | | /// are we currently inside impl_ensureControl_nothrow? |
519 | | bool m_bCreatingControl; |
520 | | |
521 | | /// the control we're responsible for |
522 | | ControlHolder m_aControl; |
523 | | |
524 | | /// the ControlContainer where we inserted our control |
525 | | Reference< XContainer > m_xContainer; |
526 | | |
527 | | /// the output device for which the control was created |
528 | | VclPtr<OutputDevice> m_pOutputDeviceForWindow; |
529 | | |
530 | | /// flag indicating whether the control is currently visible |
531 | | bool m_bControlIsVisible; |
532 | | |
533 | | /// are we currently listening at a design mode control? |
534 | | bool m_bIsDesignModeListening; |
535 | | |
536 | | enum ViewControlMode |
537 | | { |
538 | | eDesign, |
539 | | eAlive, |
540 | | eUnknown |
541 | | }; |
542 | | /// is the control currently in design mode? |
543 | | mutable ViewControlMode m_eControlDesignMode; |
544 | | |
545 | | ::basegfx::B2DHomMatrix m_aZoomLevelNormalization; |
546 | | |
547 | | public: |
548 | | explicit ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl ); |
549 | | ViewObjectContactOfUnoControl_Impl(const ViewObjectContactOfUnoControl_Impl&) = delete; |
550 | | ViewObjectContactOfUnoControl_Impl& operator=(const ViewObjectContactOfUnoControl_Impl&) = delete; |
551 | | |
552 | | /** disposes the instance, which is nonfunctional afterwards |
553 | | */ |
554 | | void dispose(); |
555 | | |
556 | | /** determines whether the instance is disposed |
557 | | */ |
558 | 0 | bool isDisposed() const { return impl_isDisposed_nofail(); } |
559 | | |
560 | | /** returns the SdrUnoObject associated with the ViewContact |
561 | | |
562 | | @precond |
563 | | We're not disposed. |
564 | | */ |
565 | | SdrUnoObj* getUnoObject() const; |
566 | | |
567 | | /** ensures that we have an ->XControl |
568 | | |
569 | | Must only be called if a control is needed when no DisplayInfo is present, yet. |
570 | | |
571 | | For creating a control, an ->OutputDevice is needed, and an ->SdrPageView. Both will be obtained |
572 | | from a ->ObjectContactOfPageView. So, if our (anti-impl's) object contact is not a ->ObjectContactOfPageView, |
573 | | this method fill fail. |
574 | | |
575 | | Failure of this method will be reported via an assertion in a non-product version. |
576 | | */ |
577 | | void ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL ); |
578 | | |
579 | | /** returns our XControl, if it already has been created |
580 | | |
581 | | If you want to ensure that the control exists before accessing it, use ->ensureControl |
582 | | */ |
583 | | const ControlHolder& |
584 | 0 | getExistentControl() const { return m_aControl; } |
585 | | |
586 | | bool |
587 | 0 | hasControl() const { return m_aControl.is(); } |
588 | | |
589 | | /** positions our XControl according to the geometry settings in the SdrUnoObj, modified by the given |
590 | | transformation, and sets proper zoom settings according to our device |
591 | | |
592 | | @precond |
593 | | ->m_pOutputDeviceForWindow and ->m_aControl are not <NULL/> |
594 | | */ |
595 | | void positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const; |
596 | | |
597 | | /** determines whether or not our control is printable |
598 | | |
599 | | Effectively, this method returns the value of the "Printable" property |
600 | | of the control's model. If we have no control, <FALSE/> is returned. |
601 | | */ |
602 | | bool isPrintableControl() const; |
603 | | |
604 | | /** sets the design mode on the control, or at least remembers the flag for the |
605 | | time the control is created |
606 | | */ |
607 | | void setControlDesignMode( bool _bDesignMode ) const; |
608 | | |
609 | | /** determines whether our control is currently visible |
610 | | @nofail |
611 | | */ |
612 | 0 | bool isControlVisible() const { return m_bControlIsVisible; } |
613 | | |
614 | | /// creates an XControl for the given device and SdrUnoObj |
615 | | static bool |
616 | | createControlForDevice( |
617 | | IPageViewAccess const & _rPageView, |
618 | | const OutputDevice& _rDevice, |
619 | | const SdrUnoObj& _rUnoObject, |
620 | | const basegfx::B2DHomMatrix& _rInitialViewTransformation, |
621 | | const basegfx::B2DHomMatrix& _rInitialZoomNormalization, |
622 | | ControlHolder& _out_rControl |
623 | | ); |
624 | | |
625 | | const ViewContactOfUnoControl& |
626 | | getViewContact() const |
627 | 0 | { |
628 | 0 | ENSURE_OR_THROW( !impl_isDisposed_nofail(), "already disposed" ); |
629 | 0 | return static_cast< const ViewContactOfUnoControl& >( m_pAntiImpl->GetViewContact() ); |
630 | 0 | } |
631 | | |
632 | | protected: |
633 | | virtual ~ViewObjectContactOfUnoControl_Impl() override; |
634 | | |
635 | | // XEventListener |
636 | | virtual void SAL_CALL disposing( const EventObject& Source ) override; |
637 | | |
638 | | // XWindowListener |
639 | | virtual void SAL_CALL windowResized( const WindowEvent& e ) override; |
640 | | virtual void SAL_CALL windowMoved( const WindowEvent& e ) override; |
641 | | virtual void SAL_CALL windowShown( const EventObject& e ) override; |
642 | | virtual void SAL_CALL windowHidden( const EventObject& e ) override; |
643 | | |
644 | | // XPropertyChangeListener |
645 | | virtual void SAL_CALL propertyChange( const PropertyChangeEvent& evt ) override; |
646 | | |
647 | | // XModeChangeListener |
648 | | virtual void SAL_CALL modeChanged( const ModeChangeEvent& _rSource ) override; |
649 | | |
650 | | // XContainerListener |
651 | | virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; |
652 | | virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; |
653 | | virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; |
654 | | |
655 | | private: |
656 | | /** retrieves the SdrPageView which our associated SdrPageViewWindow belongs to |
657 | | |
658 | | @param out_rpPageView |
659 | | a reference to a pointer holding, upon return, the desired SdrPageView |
660 | | |
661 | | @return |
662 | | <TRUE/> if and only if a ->SdrPageView could be obtained |
663 | | |
664 | | @precond |
665 | | We really belong to an SdrPageViewWindow. Perhaps (I'm not sure ATM :) |
666 | | there are instance for which this might not be true, but those instances |
667 | | should never have a need to call this method. |
668 | | |
669 | | @precond |
670 | | We're not disposed. |
671 | | |
672 | | @postcond |
673 | | The method expects success, if it returns with <FALSE/>, this will have been |
674 | | asserted. |
675 | | |
676 | | @nothrow |
677 | | */ |
678 | | bool impl_getPageView_nothrow( SdrPageView*& _out_rpPageView ); |
679 | | |
680 | | /** adjusts the control visibility so it respects its layer's visibility |
681 | | |
682 | | @precond |
683 | | ->m_aControl is not <NULL/> |
684 | | |
685 | | @precond |
686 | | We're not disposed. |
687 | | |
688 | | @precond |
689 | | We really belong to an SdrPageViewWindow. There are instance for which this |
690 | | might not be true, but those instances should never have a need to call |
691 | | this method. |
692 | | */ |
693 | | void impl_adjustControlVisibilityToLayerVisibility_throw(); |
694 | | |
695 | | /** adjusts the control visibility so it respects its layer's visibility |
696 | | |
697 | | The control must never be visible if it's in design mode. |
698 | | In alive mode, it must be visibility if and only it's on a visible layer. |
699 | | |
700 | | @param _rxControl |
701 | | the control whose visibility is to be adjusted |
702 | | |
703 | | @param _rPageView |
704 | | provides access to the attributes of the SdrPageView which the control finally belongs to |
705 | | |
706 | | @param _rUnoObject |
707 | | our SdrUnoObj |
708 | | |
709 | | @param _bIsCurrentlyVisible |
710 | | determines whether the control is currently visible. Note that this is only a shortcut for |
711 | | querying _rxControl for the XWindow2 interface, and calling isVisible at this interface. |
712 | | This shortcut has been chosen since the caller usually already has this information. |
713 | | If _bForce is <TRUE/>, _bIsCurrentlyVisible is ignored. |
714 | | |
715 | | @param _bForce |
716 | | set to <TRUE/> if you want to force a ->XWindow::setVisible call, |
717 | | no matter if the control visibility is already correct |
718 | | |
719 | | @precond |
720 | | We're not disposed. |
721 | | */ |
722 | | static void impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rxControl, const SdrUnoObj& _rUnoObject, |
723 | | IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce ); |
724 | | |
725 | | /** starts or stops listening at various aspects of our control |
726 | | |
727 | | @precond |
728 | | ->m_aControl is not <NULL/> |
729 | | */ |
730 | | void impl_switchControlListening_nothrow( bool _bStart ); |
731 | | |
732 | | /** starts or stops listening at our control container |
733 | | |
734 | | @precond |
735 | | ->m_xContainer is not <NULL/> |
736 | | */ |
737 | | void impl_switchContainerListening_nothrow( bool _bStart ); |
738 | | |
739 | | /** starts or stops listening at the control for design-mode relevant facets |
740 | | */ |
741 | | void impl_switchDesignModeListening_nothrow( bool _bStart ); |
742 | | |
743 | | /** starts or stops listening for all properties at our control |
744 | | |
745 | | @param _bStart |
746 | | determines whether to start or to stop listening |
747 | | |
748 | | @precond |
749 | | ->m_aControl is not <NULL/> |
750 | | */ |
751 | | void impl_switchPropertyListening_nothrow( bool _bStart ); |
752 | | |
753 | | /** disposes the instance |
754 | | @param _bAlsoDisposeControl |
755 | | determines whether the XControl should be disposed, too |
756 | | */ |
757 | | void impl_dispose_nothrow( bool _bAlsoDisposeControl ); |
758 | | |
759 | | /** determines whether the instance is disposed |
760 | | @nofail |
761 | | */ |
762 | 0 | bool impl_isDisposed_nofail() const { return m_pAntiImpl == nullptr; } |
763 | | |
764 | | /** determines whether the control currently is in design mode |
765 | | |
766 | | @precond |
767 | | The design mode must already be known. It is known when we first had access to |
768 | | an SdrPageView (which carries this flag), or somebody explicitly set it from |
769 | | outside. |
770 | | */ |
771 | | bool impl_isControlDesignMode_nothrow() const |
772 | 0 | { |
773 | 0 | DBG_ASSERT( m_eControlDesignMode != eUnknown, "ViewObjectContactOfUnoControl_Impl::impl_isControlDesignMode_nothrow: mode is still unknown!" ); |
774 | 0 | return m_eControlDesignMode == eDesign; |
775 | 0 | } |
776 | | |
777 | | /** ensures that we have a control for the given PageView/OutputDevice |
778 | | */ |
779 | | bool impl_ensureControl_nothrow( |
780 | | IPageViewAccess const & _rPageView, |
781 | | const OutputDevice& _rDevice, |
782 | | const basegfx::B2DHomMatrix& _rInitialViewTransformation |
783 | | ); |
784 | | |
785 | | const OutputDevice& impl_getOutputDevice_throw() const; |
786 | | }; |
787 | | |
788 | | namespace { |
789 | | |
790 | | class LazyControlCreationPrimitive2D : public ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D |
791 | | { |
792 | | private: |
793 | | typedef ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D BufferedDecompositionPrimitive2D; |
794 | | |
795 | | protected: |
796 | | virtual void |
797 | | get2DDecomposition( |
798 | | ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor, |
799 | | const ::drawinglayer::geometry::ViewInformation2D& rViewInformation |
800 | | ) const override; |
801 | | |
802 | | virtual ::drawinglayer::primitive2d::Primitive2DReference create2DDecomposition( |
803 | | const ::drawinglayer::geometry::ViewInformation2D& rViewInformation |
804 | | ) const override; |
805 | | |
806 | | virtual ::basegfx::B2DRange |
807 | | getB2DRange( |
808 | | const ::drawinglayer::geometry::ViewInformation2D& rViewInformation |
809 | | ) const override; |
810 | | |
811 | | public: |
812 | | explicit LazyControlCreationPrimitive2D( ::rtl::Reference< ViewObjectContactOfUnoControl_Impl > _pVOCImpl ) |
813 | 0 | :m_pVOCImpl(std::move( _pVOCImpl )) |
814 | 0 | { |
815 | 0 | ENSURE_OR_THROW( m_pVOCImpl.is(), "Illegal argument." ); |
816 | 0 | getTransformation( m_pVOCImpl->getViewContact(), m_aTransformation ); |
817 | 0 | } |
818 | | |
819 | | virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; |
820 | | |
821 | | // declare unique ID for this primitive class |
822 | | virtual sal_uInt32 getPrimitive2DID() const override; |
823 | | |
824 | | static void getTransformation( const ViewContactOfUnoControl& _rVOC, ::basegfx::B2DHomMatrix& _out_Transformation ); |
825 | | |
826 | | private: |
827 | | void impl_positionAndZoomControl( const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const |
828 | 0 | { |
829 | 0 | if ( !_rViewInformation.getViewport().isEmpty() ) |
830 | 0 | m_pVOCImpl->positionAndZoomControl( _rViewInformation.getObjectToViewTransformation() ); |
831 | 0 | } |
832 | | |
833 | | private: |
834 | | ::rtl::Reference< ViewObjectContactOfUnoControl_Impl > m_pVOCImpl; |
835 | | /** The geometry is part of the identity of a primitive, so we cannot calculate it on demand |
836 | | (since the data the calculation is based on might have changed then), but need to calc |
837 | | it at construction time, and remember it. |
838 | | */ |
839 | | ::basegfx::B2DHomMatrix m_aTransformation; |
840 | | }; |
841 | | |
842 | | } |
843 | | |
844 | | ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl ) |
845 | 0 | :m_pAntiImpl( _pAntiImpl ) |
846 | 0 | ,m_bCreatingControl( false ) |
847 | 0 | ,m_pOutputDeviceForWindow( nullptr ) |
848 | 0 | ,m_bControlIsVisible( false ) |
849 | 0 | ,m_bIsDesignModeListening( false ) |
850 | 0 | ,m_eControlDesignMode( eUnknown ) |
851 | 0 | { |
852 | 0 | DBG_ASSERT( m_pAntiImpl, "ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl: invalid AntiImpl!" ); |
853 | |
|
854 | 0 | const OutputDevice& rPageViewDevice( impl_getOutputDevice_throw() ); |
855 | 0 | m_aZoomLevelNormalization = rPageViewDevice.GetInverseViewTransformation(); |
856 | |
|
857 | | #if OSL_DEBUG_LEVEL > 0 |
858 | | ::basegfx::B2DVector aScale, aTranslate; |
859 | | double fRotate, fShearX; |
860 | | m_aZoomLevelNormalization.decompose( aScale, aTranslate, fRotate, fShearX ); |
861 | | #endif |
862 | |
|
863 | 0 | ::basegfx::B2DHomMatrix aScaleNormalization; |
864 | 0 | const MapMode& aCurrentDeviceMapMode( rPageViewDevice.GetMapMode() ); |
865 | 0 | aScaleNormalization.set( 0, 0, static_cast<double>(aCurrentDeviceMapMode.GetScaleX()) ); |
866 | 0 | aScaleNormalization.set( 1, 1, static_cast<double>(aCurrentDeviceMapMode.GetScaleY()) ); |
867 | 0 | m_aZoomLevelNormalization *= aScaleNormalization; |
868 | |
|
869 | | #if OSL_DEBUG_LEVEL > 0 |
870 | | m_aZoomLevelNormalization.decompose( aScale, aTranslate, fRotate, fShearX ); |
871 | | #endif |
872 | 0 | } |
873 | | |
874 | | |
875 | | ViewObjectContactOfUnoControl_Impl::~ViewObjectContactOfUnoControl_Impl() |
876 | 0 | { |
877 | 0 | if ( !impl_isDisposed_nofail() ) |
878 | 0 | { |
879 | 0 | acquire(); |
880 | 0 | dispose(); |
881 | 0 | } |
882 | |
|
883 | 0 | } |
884 | | |
885 | | |
886 | | void ViewObjectContactOfUnoControl_Impl::impl_dispose_nothrow( bool _bAlsoDisposeControl ) |
887 | 0 | { |
888 | 0 | if ( impl_isDisposed_nofail() ) |
889 | 0 | return; |
890 | | |
891 | 0 | if ( m_aControl.is() ) |
892 | 0 | impl_switchControlListening_nothrow( false ); |
893 | |
|
894 | 0 | if ( m_xContainer.is() ) |
895 | 0 | impl_switchContainerListening_nothrow( false ); |
896 | | |
897 | | // dispose the control |
898 | 0 | if ( _bAlsoDisposeControl ) |
899 | 0 | UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl ); |
900 | |
|
901 | 0 | m_aControl.clear(); |
902 | 0 | m_xContainer.clear(); |
903 | 0 | m_pOutputDeviceForWindow = nullptr; |
904 | 0 | m_bControlIsVisible = false; |
905 | |
|
906 | 0 | m_pAntiImpl = nullptr; |
907 | 0 | } |
908 | | |
909 | | |
910 | | void ViewObjectContactOfUnoControl_Impl::dispose() |
911 | 0 | { |
912 | 0 | SolarMutexGuard aSolarGuard; |
913 | 0 | impl_dispose_nothrow( true ); |
914 | 0 | } |
915 | | |
916 | | |
917 | | SdrUnoObj* ViewObjectContactOfUnoControl_Impl::getUnoObject() const |
918 | 0 | { |
919 | 0 | OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::getUnoObject: already disposed()" ); |
920 | 0 | if ( impl_isDisposed_nofail() ) |
921 | 0 | return nullptr; |
922 | 0 | auto pRet = dynamic_cast< SdrUnoObj* >( m_pAntiImpl->GetViewContact().TryToGetSdrObject() ); |
923 | 0 | DBG_ASSERT( pRet || !m_pAntiImpl->GetViewContact().TryToGetSdrObject(), |
924 | 0 | "ViewObjectContactOfUnoControl_Impl::getUnoObject: invalid SdrObject!" ); |
925 | 0 | return pRet; |
926 | 0 | } |
927 | | |
928 | | |
929 | | void ViewObjectContactOfUnoControl_Impl::positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const |
930 | 0 | { |
931 | 0 | OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no output device or no control!" ); |
932 | 0 | if ( !m_aControl.is() ) |
933 | 0 | return; |
934 | | |
935 | 0 | try |
936 | 0 | { |
937 | 0 | SdrUnoObj* pUnoObject = getUnoObject(); |
938 | 0 | if ( pUnoObject ) |
939 | 0 | { |
940 | 0 | const tools::Rectangle aRect( pUnoObject->GetLogicRect() ); |
941 | 0 | UnoControlContactHelper::adjustControlGeometry_throw( m_aControl, aRect, _rViewTransformation, m_aZoomLevelNormalization ); |
942 | 0 | } |
943 | 0 | else |
944 | 0 | OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no SdrUnoObj!" ); |
945 | 0 | } |
946 | 0 | catch( const Exception& ) |
947 | 0 | { |
948 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
949 | 0 | } |
950 | 0 | } |
951 | | |
952 | | |
953 | | void ViewObjectContactOfUnoControl_Impl::ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL ) |
954 | 0 | { |
955 | 0 | OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::ensureControl: already disposed()" ); |
956 | 0 | if ( impl_isDisposed_nofail() ) |
957 | 0 | return; |
958 | | |
959 | 0 | ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &m_pAntiImpl->GetObjectContact() ); |
960 | 0 | if ( pPageViewContact ) |
961 | 0 | { |
962 | 0 | SdrPageViewAccess aPVAccess( pPageViewContact->GetPageWindow().GetPageView() ); |
963 | 0 | const OutputDevice& rDevice( *m_pAntiImpl->getPageViewOutputDevice() ); |
964 | 0 | impl_ensureControl_nothrow( |
965 | 0 | aPVAccess, |
966 | 0 | rDevice, |
967 | 0 | _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation() |
968 | 0 | ); |
969 | 0 | return; |
970 | 0 | } |
971 | | |
972 | 0 | DummyPageViewAccess aNoPageView; |
973 | 0 | const OutputDevice& rDevice( impl_getOutputDevice_throw() ); |
974 | 0 | impl_ensureControl_nothrow( |
975 | 0 | aNoPageView, |
976 | 0 | rDevice, |
977 | 0 | _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation() |
978 | 0 | ); |
979 | 0 | } |
980 | | |
981 | | |
982 | | const OutputDevice& ViewObjectContactOfUnoControl_Impl::impl_getOutputDevice_throw() const |
983 | 0 | { |
984 | | // do not use ObjectContact::TryToGetOutputDevice, it would not care for the PageWindow's |
985 | | // OriginalPaintWindow |
986 | 0 | const OutputDevice* oPageOutputDev = m_pAntiImpl->getPageViewOutputDevice(); |
987 | 0 | if( oPageOutputDev ) |
988 | 0 | return *oPageOutputDev; |
989 | | |
990 | 0 | const OutputDevice* pDevice = m_pAntiImpl->GetObjectContact().TryToGetOutputDevice(); |
991 | 0 | ENSURE_OR_THROW( pDevice, "no output device -> no control" ); |
992 | 0 | return *pDevice; |
993 | 0 | } |
994 | | |
995 | | bool ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow( IPageViewAccess const & _rPageView, const OutputDevice& _rDevice, |
996 | | const basegfx::B2DHomMatrix& _rInitialViewTransformation ) |
997 | 0 | { |
998 | 0 | if ( m_bCreatingControl ) |
999 | 0 | { |
1000 | 0 | OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: reentrance is not really good here!" ); |
1001 | | // We once had a situation where this was called reentrantly, which lead to all kind of strange effects. All |
1002 | | // those affected the grid control, which is the only control so far which is visible in design mode (and |
1003 | | // not only in alive mode). |
1004 | | // Creating the control triggered a Window::Update on some of its child windows, which triggered a |
1005 | | // Paint on parent of the grid control (e.g. the SwEditWin), which triggered a reentrant call to this method, |
1006 | | // which it is not really prepared for. |
1007 | | |
1008 | | // /me thinks that re-entrance should be caught on a higher level, i.e. the Drawing Layer should not allow |
1009 | | // reentrant paint requests. For the moment, until /me can discuss this with AW, catch it here. #i104544# |
1010 | 0 | return false; |
1011 | 0 | } |
1012 | | |
1013 | | // We are creating the control from this point on. |
1014 | 0 | comphelper::FlagRestorationGuard aControlGuard(m_bCreatingControl, true); |
1015 | |
|
1016 | 0 | if ( m_aControl.is() ) |
1017 | 0 | { |
1018 | 0 | if ( m_pOutputDeviceForWindow.get() == &_rDevice ) |
1019 | 0 | return true; |
1020 | | |
1021 | | // Somebody requested a control for a new device, which means either of |
1022 | | // - our PageView's paint window changed since we were last here |
1023 | | // - we don't belong to a page view, and are simply painted onto different devices |
1024 | | // The first sounds strange (doesn't it?), the second means we could perhaps |
1025 | | // optimize this in the future - there is no need to re-create the control every time, |
1026 | | // is it? #i74523# |
1027 | 0 | if ( m_xContainer.is() ) |
1028 | 0 | impl_switchContainerListening_nothrow( false ); |
1029 | 0 | impl_switchControlListening_nothrow( false ); |
1030 | 0 | UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl ); |
1031 | 0 | } |
1032 | | |
1033 | 0 | SdrUnoObj* pUnoObject = getUnoObject(); |
1034 | 0 | if ( !pUnoObject ) |
1035 | 0 | return false; |
1036 | | |
1037 | 0 | ControlHolder aControl; |
1038 | 0 | if ( !createControlForDevice( _rPageView, _rDevice, *pUnoObject, _rInitialViewTransformation, m_aZoomLevelNormalization, aControl ) ) |
1039 | 0 | return false; |
1040 | | |
1041 | 0 | m_pOutputDeviceForWindow = const_cast< OutputDevice * >( &_rDevice ); |
1042 | 0 | m_aControl = std::move(aControl); |
1043 | 0 | m_xContainer.set(_rPageView.getControlContainer( _rDevice ), css::uno::UNO_QUERY); |
1044 | 0 | DBG_ASSERT( ( m_xContainer.is() // either have a XControlContainer |
1045 | 0 | || ( ( !_rPageView.getControlContainer( _rDevice ).is() ) // or don't have any container, |
1046 | 0 | && ( _rDevice.GetOwnerWindow() == nullptr ) // which is allowed for non-Window instances only |
1047 | 0 | ) |
1048 | 0 | ), |
1049 | 0 | "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: no XContainer at the ControlContainer!" ); |
1050 | |
|
1051 | 0 | try |
1052 | 0 | { |
1053 | 0 | m_eControlDesignMode = m_aControl.isDesignMode() ? eDesign : eAlive; |
1054 | 0 | m_bControlIsVisible = m_aControl.isVisible(); |
1055 | 0 | } |
1056 | 0 | catch( const Exception& ) |
1057 | 0 | { |
1058 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1059 | 0 | } |
1060 | | |
1061 | | // start listening at all aspects of the control which are interesting to us ... |
1062 | 0 | impl_switchControlListening_nothrow( true ); |
1063 | | |
1064 | | // start listening at the control container, in case somebody tampers with our control |
1065 | 0 | if ( m_xContainer.is() ) |
1066 | 0 | impl_switchContainerListening_nothrow( true ); |
1067 | |
|
1068 | 0 | return m_aControl.is(); |
1069 | 0 | } |
1070 | | |
1071 | | |
1072 | | bool ViewObjectContactOfUnoControl_Impl::createControlForDevice( IPageViewAccess const & _rPageView, |
1073 | | const OutputDevice& _rDevice, const SdrUnoObj& _rUnoObject, const basegfx::B2DHomMatrix& _rInitialViewTransformation, |
1074 | | const basegfx::B2DHomMatrix& _rInitialZoomNormalization, ControlHolder& _out_rControl ) |
1075 | 0 | { |
1076 | 0 | _out_rControl.clear(); |
1077 | |
|
1078 | 0 | const Reference< XControlModel >& xControlModel( _rUnoObject.GetUnoControlModel() ); |
1079 | 0 | DBG_ASSERT( xControlModel.is(), "ViewObjectContactOfUnoControl_Impl::createControlForDevice: no control model at the SdrUnoObject!?" ); |
1080 | 0 | if ( !xControlModel.is() ) |
1081 | 0 | return false; |
1082 | | |
1083 | 0 | bool bSuccess = false; |
1084 | 0 | try |
1085 | 0 | { |
1086 | 0 | const OUString& sControlServiceName( _rUnoObject.GetUnoControlTypeName() ); |
1087 | |
|
1088 | 0 | const Reference< css::uno::XComponentContext >& xContext = ::comphelper::getProcessComponentContext(); |
1089 | 0 | _out_rControl = Reference<XControl>( xContext->getServiceManager()->createInstanceWithContext(sControlServiceName, xContext), UNO_QUERY_THROW ); |
1090 | | |
1091 | | // tdf#150886 for calc/writer/impress make forms ignore the platform theme |
1092 | 0 | Reference<XPropertySet> xModelProperties(xControlModel, UNO_QUERY); |
1093 | 0 | Reference<XPropertySetInfo> xInfo = xModelProperties ? xModelProperties->getPropertySetInfo() : nullptr; |
1094 | 0 | if (xInfo && xInfo->hasPropertyByName(u"StandardTheme"_ustr)) |
1095 | 0 | xModelProperties->setPropertyValue(u"StandardTheme"_ustr, Any(!_rUnoObject.getSdrModelFromSdrObject().AreControlsThemed())); |
1096 | | |
1097 | | // knit the model and the control |
1098 | 0 | _out_rControl.setModel( xControlModel ); |
1099 | 0 | const tools::Rectangle aRect( _rUnoObject.GetLogicRect() ); |
1100 | | |
1101 | | // proper geometry |
1102 | 0 | UnoControlContactHelper::adjustControlGeometry_throw( |
1103 | 0 | _out_rControl, |
1104 | 0 | aRect, |
1105 | 0 | _rInitialViewTransformation, |
1106 | 0 | _rInitialZoomNormalization |
1107 | 0 | ); |
1108 | | |
1109 | | // set design mode before peer is created, |
1110 | | // this is also needed for accessibility |
1111 | 0 | _out_rControl.setDesignMode( _rPageView.isDesignMode() ); |
1112 | | |
1113 | | // adjust the initial visibility according to the visibility of the layer |
1114 | 0 | impl_adjustControlVisibilityToLayerVisibility_throw( _out_rControl, _rUnoObject, _rPageView, false, true ); |
1115 | | |
1116 | | // add the control to the respective control container |
1117 | | // do this last |
1118 | 0 | Reference< XControlContainer > xControlContainer( _rPageView.getControlContainer( _rDevice ) ); |
1119 | 0 | if ( xControlContainer.is() ) |
1120 | 0 | xControlContainer->addControl( sControlServiceName, _out_rControl.getControl() ); |
1121 | |
|
1122 | 0 | bSuccess = true; |
1123 | 0 | } |
1124 | 0 | catch( const Exception& ) |
1125 | 0 | { |
1126 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1127 | 0 | } |
1128 | |
|
1129 | 0 | if ( !bSuccess ) |
1130 | 0 | { |
1131 | | // delete the control which might have been created already |
1132 | 0 | UnoControlContactHelper::disposeAndClearControl_nothrow( _out_rControl ); |
1133 | 0 | } |
1134 | |
|
1135 | 0 | return _out_rControl.is(); |
1136 | 0 | } |
1137 | | |
1138 | | |
1139 | | bool ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow( SdrPageView*& _out_rpPageView ) |
1140 | 0 | { |
1141 | 0 | OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: already disposed!" ); |
1142 | |
|
1143 | 0 | _out_rpPageView = nullptr; |
1144 | 0 | if ( impl_isDisposed_nofail() ) |
1145 | 0 | return false; |
1146 | | |
1147 | 0 | ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &m_pAntiImpl->GetObjectContact() ); |
1148 | 0 | if ( pPageViewContact ) |
1149 | 0 | _out_rpPageView = &pPageViewContact->GetPageWindow().GetPageView(); |
1150 | |
|
1151 | 0 | DBG_ASSERT( _out_rpPageView != nullptr, "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: this method is expected to always have success!" ); |
1152 | 0 | return ( _out_rpPageView != nullptr ); |
1153 | 0 | } |
1154 | | |
1155 | | |
1156 | | void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw() |
1157 | 0 | { |
1158 | 0 | OSL_PRECOND( m_aControl.is(), |
1159 | 0 | "ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw: only valid if we have a control!" ); |
1160 | |
|
1161 | 0 | SdrPageView* pPageView( nullptr ); |
1162 | 0 | if ( !impl_getPageView_nothrow( pPageView ) ) |
1163 | 0 | return; |
1164 | | |
1165 | 0 | SdrUnoObj* pUnoObject = getUnoObject(); |
1166 | 0 | if ( !pUnoObject ) |
1167 | 0 | return; |
1168 | | |
1169 | 0 | SdrPageViewAccess aPVAccess( *pPageView ); |
1170 | 0 | impl_adjustControlVisibilityToLayerVisibility_throw( m_aControl, *pUnoObject, aPVAccess, m_bControlIsVisible, false/*_bForce*/ ); |
1171 | 0 | } |
1172 | | |
1173 | | |
1174 | | void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rControl, |
1175 | | const SdrUnoObj& _rUnoObject, IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce ) |
1176 | 0 | { |
1177 | | // in design mode, there is no problem with the visibility: The XControl is hidden by |
1178 | | // default, and the Drawing Layer will simply not call our paint routine, if we're in |
1179 | | // a hidden layer. So, only alive mode matters. |
1180 | 0 | if ( !_rControl.isDesignMode() ) |
1181 | 0 | { |
1182 | | // the layer of our object |
1183 | 0 | SdrLayerID nObjectLayer = _rUnoObject.GetLayer(); |
1184 | | // is the object we're residing in visible in this view? |
1185 | 0 | bool bIsObjectVisible = _rUnoObject.IsVisible() && _rPageView.isLayerVisible( nObjectLayer ); |
1186 | |
|
1187 | 0 | if ( _bForce || ( bIsObjectVisible != _bIsCurrentlyVisible ) ) |
1188 | 0 | { |
1189 | 0 | _rControl.setVisible( bIsObjectVisible ); |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | } |
1193 | | |
1194 | | |
1195 | | void ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow( bool _bStart ) |
1196 | 0 | { |
1197 | 0 | OSL_PRECOND( m_xContainer.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow: no control container!" ); |
1198 | 0 | if ( !m_xContainer.is() ) |
1199 | 0 | return; |
1200 | | |
1201 | 0 | try |
1202 | 0 | { |
1203 | 0 | if ( _bStart ) |
1204 | 0 | m_xContainer->addContainerListener( this ); |
1205 | 0 | else |
1206 | 0 | m_xContainer->removeContainerListener( this ); |
1207 | 0 | } |
1208 | 0 | catch( const Exception& ) |
1209 | 0 | { |
1210 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1211 | 0 | } |
1212 | 0 | } |
1213 | | |
1214 | | |
1215 | | void ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow( bool _bStart ) |
1216 | 0 | { |
1217 | 0 | OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow: invalid control!" ); |
1218 | 0 | if ( !m_aControl.is() ) |
1219 | 0 | return; |
1220 | | |
1221 | 0 | try |
1222 | 0 | { |
1223 | | // listen for visibility changes |
1224 | 0 | if ( _bStart ) |
1225 | 0 | m_aControl.addWindowListener( this ); |
1226 | 0 | else |
1227 | 0 | m_aControl.removeWindowListener( this ); |
1228 | | |
1229 | | // in design mode, listen for some more aspects |
1230 | 0 | impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() && _bStart ); |
1231 | | |
1232 | | // listen for design mode changes |
1233 | 0 | Reference< XModeChangeBroadcaster > xDesignModeChanges( m_aControl.getControl(), UNO_QUERY_THROW ); |
1234 | 0 | if ( _bStart ) |
1235 | 0 | xDesignModeChanges->addModeChangeListener( this ); |
1236 | 0 | else |
1237 | 0 | xDesignModeChanges->removeModeChangeListener( this ); |
1238 | 0 | } |
1239 | 0 | catch( const Exception& ) |
1240 | 0 | { |
1241 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1242 | 0 | } |
1243 | 0 | } |
1244 | | |
1245 | | |
1246 | | void ViewObjectContactOfUnoControl_Impl::impl_switchDesignModeListening_nothrow( bool _bStart ) |
1247 | 0 | { |
1248 | 0 | if ( m_bIsDesignModeListening != _bStart ) |
1249 | 0 | { |
1250 | 0 | m_bIsDesignModeListening = _bStart; |
1251 | 0 | impl_switchPropertyListening_nothrow( _bStart ); |
1252 | 0 | } |
1253 | 0 | } |
1254 | | |
1255 | | |
1256 | | void ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow( bool _bStart ) |
1257 | 0 | { |
1258 | 0 | OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow: no control!" ); |
1259 | 0 | if ( !m_aControl.is() ) |
1260 | 0 | return; |
1261 | | |
1262 | 0 | try |
1263 | 0 | { |
1264 | 0 | Reference< XPropertySet > xModelProperties( m_aControl.getModel(), UNO_QUERY_THROW ); |
1265 | 0 | if ( _bStart ) |
1266 | 0 | xModelProperties->addPropertyChangeListener( OUString(), this ); |
1267 | 0 | else |
1268 | 0 | xModelProperties->removePropertyChangeListener( OUString(), this ); |
1269 | 0 | } |
1270 | 0 | catch( const Exception& ) |
1271 | 0 | { |
1272 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1273 | 0 | } |
1274 | 0 | } |
1275 | | |
1276 | | |
1277 | | bool ViewObjectContactOfUnoControl_Impl::isPrintableControl() const |
1278 | 0 | { |
1279 | 0 | SdrUnoObj* pUnoObject = getUnoObject(); |
1280 | 0 | if ( !pUnoObject ) |
1281 | 0 | return false; |
1282 | | |
1283 | 0 | bool bIsPrintable = false; |
1284 | 0 | try |
1285 | 0 | { |
1286 | 0 | Reference< XPropertySet > xModelProperties( pUnoObject->GetUnoControlModel(), UNO_QUERY_THROW ); |
1287 | 0 | OSL_VERIFY( xModelProperties->getPropertyValue( u"Printable"_ustr ) >>= bIsPrintable ); |
1288 | 0 | } |
1289 | 0 | catch( const Exception& ) |
1290 | 0 | { |
1291 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1292 | 0 | } |
1293 | 0 | return bIsPrintable; |
1294 | 0 | } |
1295 | | |
1296 | | |
1297 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::disposing( const EventObject& Source ) |
1298 | 0 | { |
1299 | 0 | SolarMutexGuard aSolarGuard; |
1300 | | // some code below - in particular our disposal - might trigger actions which require the |
1301 | | // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control, |
1302 | | // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex - |
1303 | | // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169# |
1304 | |
|
1305 | 0 | if ( !m_aControl.is() ) |
1306 | 0 | return; |
1307 | | |
1308 | 0 | if ( ( m_aControl == Source.Source ) |
1309 | 0 | || ( m_aControl.getModel() == Source.Source ) |
1310 | 0 | ) |
1311 | 0 | { |
1312 | | // the model or the control is dying ... hmm, not much sense in that we ourself continue |
1313 | | // living |
1314 | 0 | impl_dispose_nothrow( false ); |
1315 | 0 | return; |
1316 | 0 | } |
1317 | | |
1318 | 0 | DBG_ASSERT( Source.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::disposing: Who's this?" ); |
1319 | 0 | } |
1320 | | |
1321 | | |
1322 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowResized( const WindowEvent& /*e*/ ) |
1323 | 0 | { |
1324 | | // not interested in |
1325 | 0 | } |
1326 | | |
1327 | | |
1328 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowMoved( const WindowEvent& /*e*/ ) |
1329 | 0 | { |
1330 | | // not interested in |
1331 | 0 | } |
1332 | | |
1333 | | |
1334 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowShown( const EventObject& /*e*/ ) |
1335 | 0 | { |
1336 | 0 | SolarMutexGuard aSolarGuard; |
1337 | 0 | m_bControlIsVisible = true; |
1338 | 0 | } |
1339 | | |
1340 | | |
1341 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowHidden( const EventObject& /*e*/ ) |
1342 | 0 | { |
1343 | 0 | SolarMutexGuard aSolarGuard; |
1344 | 0 | m_bControlIsVisible = false; |
1345 | 0 | } |
1346 | | |
1347 | | |
1348 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::propertyChange( const PropertyChangeEvent& /*_rEvent*/ ) |
1349 | 0 | { |
1350 | 0 | SolarMutexGuard aSolarGuard; |
1351 | | // (re)painting might require VCL operations, which need the SolarMutex |
1352 | |
|
1353 | 0 | OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::propertyChange: already disposed()" ); |
1354 | 0 | if ( impl_isDisposed_nofail() ) |
1355 | 0 | return; |
1356 | | |
1357 | 0 | DBG_ASSERT( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::propertyChange: " ); |
1358 | 0 | if ( !m_aControl.is() ) |
1359 | 0 | return; |
1360 | | |
1361 | | // a generic property changed. If we're in design mode, we need to repaint the control |
1362 | 0 | if ( impl_isControlDesignMode_nothrow() ) |
1363 | 0 | { |
1364 | 0 | m_pAntiImpl->propertyChange(); |
1365 | 0 | } |
1366 | 0 | } |
1367 | | |
1368 | | |
1369 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::modeChanged( const ModeChangeEvent& _rSource ) |
1370 | 0 | { |
1371 | 0 | SolarMutexGuard aSolarGuard; |
1372 | |
|
1373 | 0 | DBG_ASSERT( _rSource.NewMode == "design" || _rSource.NewMode == "alive", "ViewObjectContactOfUnoControl_Impl::modeChanged: unexpected mode!" ); |
1374 | |
|
1375 | 0 | m_eControlDesignMode = _rSource.NewMode == "design" ? eDesign : eAlive; |
1376 | |
|
1377 | 0 | impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() ); |
1378 | |
|
1379 | 0 | try |
1380 | 0 | { |
1381 | | // if the control is part of an invisible layer, we need to explicitly hide it in alive mode |
1382 | 0 | impl_adjustControlVisibilityToLayerVisibility_throw(); |
1383 | 0 | } |
1384 | 0 | catch( const Exception& ) |
1385 | 0 | { |
1386 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1387 | 0 | } |
1388 | 0 | } |
1389 | | |
1390 | | |
1391 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementInserted( const ContainerEvent& /*_Event*/ ) |
1392 | 0 | { |
1393 | | // not interested in |
1394 | 0 | } |
1395 | | |
1396 | | |
1397 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementRemoved( const ContainerEvent& Event ) |
1398 | 0 | { |
1399 | 0 | SolarMutexGuard aSolarGuard; |
1400 | | // some code below - in particular our disposal - might trigger actions which require the |
1401 | | // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control, |
1402 | | // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex - |
1403 | | // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169# |
1404 | 0 | DBG_ASSERT( Event.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::elementRemoved: where did this come from?" ); |
1405 | |
|
1406 | 0 | if ( m_aControl == Event.Element ) |
1407 | 0 | impl_dispose_nothrow( false ); |
1408 | 0 | } |
1409 | | |
1410 | | |
1411 | | void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementReplaced( const ContainerEvent& Event ) |
1412 | 0 | { |
1413 | 0 | SolarMutexGuard aSolarGuard; |
1414 | 0 | DBG_ASSERT( Event.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::elementReplaced: where did this come from?" ); |
1415 | |
|
1416 | 0 | if ( ! ( m_aControl == Event.ReplacedElement ) ) |
1417 | 0 | return; |
1418 | | |
1419 | 0 | Reference< XControl > xNewControl( Event.Element, UNO_QUERY ); |
1420 | 0 | DBG_ASSERT( xNewControl.is(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: invalid new control!" ); |
1421 | 0 | if ( !xNewControl.is() ) |
1422 | 0 | return; |
1423 | | |
1424 | 0 | ENSURE_OR_THROW( m_pOutputDeviceForWindow, "calling this without /me having an output device should be impossible." ); |
1425 | |
|
1426 | 0 | DBG_ASSERT( xNewControl->getModel() == m_aControl.getModel(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: another model at the new control?" ); |
1427 | | // another model should - in the drawing layer - also imply another SdrUnoObj, which |
1428 | | // should also result in new ViewContact, and thus in new ViewObjectContacts |
1429 | |
|
1430 | 0 | impl_switchControlListening_nothrow( false ); |
1431 | |
|
1432 | 0 | ControlHolder aNewControl( xNewControl ); |
1433 | 0 | aNewControl.setZoom( m_aControl.getZoom() ); |
1434 | 0 | aNewControl.setPosSize( m_aControl.getPosSize() ); |
1435 | 0 | aNewControl.setDesignMode( impl_isControlDesignMode_nothrow() ); |
1436 | |
|
1437 | 0 | m_aControl = xNewControl; |
1438 | 0 | m_bControlIsVisible = m_aControl.isVisible(); |
1439 | |
|
1440 | 0 | impl_switchControlListening_nothrow( true ); |
1441 | |
|
1442 | 0 | m_pAntiImpl->onControlChangedOrModified( ViewObjectContactOfUnoControl::ImplAccess() ); |
1443 | 0 | } |
1444 | | |
1445 | | |
1446 | | void ViewObjectContactOfUnoControl_Impl::setControlDesignMode( bool _bDesignMode ) const |
1447 | 0 | { |
1448 | 0 | if ( ( m_eControlDesignMode != eUnknown ) && ( _bDesignMode == impl_isControlDesignMode_nothrow() ) ) |
1449 | | // nothing to do |
1450 | 0 | return; |
1451 | 0 | m_eControlDesignMode = _bDesignMode ? eDesign : eAlive; |
1452 | |
|
1453 | 0 | if ( !m_aControl.is() ) |
1454 | | // nothing to do, the setting will be respected as soon as the control |
1455 | | // is created |
1456 | 0 | return; |
1457 | | |
1458 | 0 | try |
1459 | 0 | { |
1460 | 0 | m_aControl.setDesignMode( _bDesignMode ); |
1461 | 0 | } |
1462 | 0 | catch( const Exception& ) |
1463 | 0 | { |
1464 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1465 | 0 | } |
1466 | 0 | } |
1467 | | |
1468 | | |
1469 | | //= LazyControlCreationPrimitive2D |
1470 | | |
1471 | | |
1472 | | bool LazyControlCreationPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const |
1473 | 0 | { |
1474 | 0 | if ( !BufferedDecompositionPrimitive2D::operator==( rPrimitive ) ) |
1475 | 0 | return false; |
1476 | | |
1477 | 0 | const LazyControlCreationPrimitive2D* pRHS = dynamic_cast< const LazyControlCreationPrimitive2D* >( &rPrimitive ); |
1478 | 0 | if ( !pRHS ) |
1479 | 0 | return false; |
1480 | | |
1481 | 0 | if ( m_pVOCImpl != pRHS->m_pVOCImpl ) |
1482 | 0 | return false; |
1483 | | |
1484 | 0 | if ( m_aTransformation != pRHS->m_aTransformation ) |
1485 | 0 | return false; |
1486 | | |
1487 | 0 | return true; |
1488 | 0 | } |
1489 | | |
1490 | | |
1491 | | void LazyControlCreationPrimitive2D::getTransformation( const ViewContactOfUnoControl& _rVOC, ::basegfx::B2DHomMatrix& _out_Transformation ) |
1492 | 0 | { |
1493 | | // Do use model data directly to create the correct geometry. Do NOT |
1494 | | // use getBoundRect()/getSnapRect() here; these will use the sequence of |
1495 | | // primitives themselves in the long run. |
1496 | 0 | const tools::Rectangle aSdrGeoData( _rVOC.GetSdrUnoObj().GetGeoRect() ); |
1497 | 0 | const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aSdrGeoData); |
1498 | |
|
1499 | 0 | _out_Transformation.identity(); |
1500 | 0 | _out_Transformation.set( 0, 0, aRange.getWidth() ); |
1501 | 0 | _out_Transformation.set( 1, 1, aRange.getHeight() ); |
1502 | 0 | _out_Transformation.set( 0, 2, aRange.getMinX() ); |
1503 | 0 | _out_Transformation.set( 1, 2, aRange.getMinY() ); |
1504 | 0 | } |
1505 | | |
1506 | | |
1507 | | ::basegfx::B2DRange LazyControlCreationPrimitive2D::getB2DRange( const ::drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/ ) const |
1508 | 0 | { |
1509 | 0 | ::basegfx::B2DRange aRange( 0.0, 0.0, 1.0, 1.0 ); |
1510 | 0 | aRange.transform( m_aTransformation ); |
1511 | 0 | return aRange; |
1512 | 0 | } |
1513 | | |
1514 | | |
1515 | | void LazyControlCreationPrimitive2D::get2DDecomposition( ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor, const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const |
1516 | 0 | { |
1517 | | #if OSL_DEBUG_LEVEL > 0 |
1518 | | ::basegfx::B2DVector aScale, aTranslate; |
1519 | | double fRotate, fShearX; |
1520 | | _rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX ); |
1521 | | #endif |
1522 | 0 | if ( m_pVOCImpl->hasControl() ) |
1523 | 0 | impl_positionAndZoomControl( _rViewInformation ); |
1524 | 0 | BufferedDecompositionPrimitive2D::get2DDecomposition( rVisitor, _rViewInformation ); |
1525 | 0 | } |
1526 | | |
1527 | | |
1528 | | ::drawinglayer::primitive2d::Primitive2DReference LazyControlCreationPrimitive2D::create2DDecomposition( const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const |
1529 | 0 | { |
1530 | | #if OSL_DEBUG_LEVEL > 0 |
1531 | | ::basegfx::B2DVector aScale, aTranslate; |
1532 | | double fRotate, fShearX; |
1533 | | _rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX ); |
1534 | | #endif |
1535 | 0 | const bool bHadControl = m_pVOCImpl->getExistentControl().is(); |
1536 | | |
1537 | | // force control here to make it a VCL ChildWindow. Will be fetched |
1538 | | // and used below by getExistentControl() |
1539 | 0 | m_pVOCImpl->ensureControl( &_rViewInformation.getObjectToViewTransformation() ); |
1540 | 0 | impl_positionAndZoomControl( _rViewInformation ); |
1541 | | |
1542 | | // get needed data |
1543 | 0 | const ViewContactOfUnoControl& rViewContactOfUnoControl( m_pVOCImpl->getViewContact() ); |
1544 | 0 | Reference< XControlModel > xControlModel( rViewContactOfUnoControl.GetSdrUnoObj().GetUnoControlModel() ); |
1545 | 0 | const ControlHolder& rControl( m_pVOCImpl->getExistentControl() ); |
1546 | |
|
1547 | 0 | if ( !bHadControl && rControl.is() && rControl.isVisible() ) |
1548 | 0 | rControl.invalidate(); |
1549 | | |
1550 | | // check if we already have an XControl. |
1551 | 0 | if ( !xControlModel.is() || !rControl.is() ) |
1552 | 0 | { |
1553 | | // use the default mechanism. This will create a ControlPrimitive2D without |
1554 | | // handing over a XControl. If not even a XControlModel exists, it will |
1555 | | // create the SdrObject fallback visualisation |
1556 | 0 | ::drawinglayer::primitive2d::Primitive2DContainer aContainer; |
1557 | 0 | rViewContactOfUnoControl.getViewIndependentPrimitive2DContainer(aContainer); |
1558 | 0 | return new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer)); |
1559 | 0 | } |
1560 | | |
1561 | 0 | SdrObject const& rSdrObj(m_pVOCImpl->getViewContact().GetSdrObject()); |
1562 | 0 | void const* pAnchorKey(nullptr); |
1563 | 0 | if (auto const pUserCall = rSdrObj.GetUserCall()) |
1564 | 0 | { |
1565 | 0 | pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(rSdrObj); |
1566 | 0 | } |
1567 | | |
1568 | | // create a primitive and hand over the existing xControl. This will |
1569 | | // allow the primitive to not need to create another one on demand. |
1570 | 0 | return new ::drawinglayer::primitive2d::ControlPrimitive2D( |
1571 | 0 | m_aTransformation, xControlModel, rControl.getControl(), |
1572 | 0 | rSdrObj.GetTitle(), rSdrObj.GetDescription(), pAnchorKey); |
1573 | 0 | } |
1574 | | |
1575 | | sal_uInt32 LazyControlCreationPrimitive2D::getPrimitive2DID() const |
1576 | 0 | { |
1577 | 0 | return PRIMITIVE2D_ID_SDRCONTROLPRIMITIVE2D; |
1578 | 0 | } |
1579 | | |
1580 | | ViewObjectContactOfUnoControl::ViewObjectContactOfUnoControl( ObjectContact& _rObjectContact, ViewContactOfUnoControl& _rViewContact ) |
1581 | 0 | :ViewObjectContactOfSdrObj( _rObjectContact, _rViewContact ) |
1582 | 0 | ,m_pImpl( new ViewObjectContactOfUnoControl_Impl( this ) ) |
1583 | 0 | { |
1584 | 0 | } |
1585 | | |
1586 | | |
1587 | | ViewObjectContactOfUnoControl::~ViewObjectContactOfUnoControl() |
1588 | 0 | { |
1589 | 0 | m_pImpl->dispose(); |
1590 | 0 | m_pImpl = nullptr; |
1591 | |
|
1592 | 0 | } |
1593 | | |
1594 | | |
1595 | | Reference< XControl > ViewObjectContactOfUnoControl::getControl() |
1596 | 0 | { |
1597 | 0 | SolarMutexGuard aSolarGuard; |
1598 | 0 | m_pImpl->ensureControl( nullptr ); |
1599 | 0 | return m_pImpl->getExistentControl().getControl(); |
1600 | 0 | } |
1601 | | |
1602 | | |
1603 | | Reference< XControl > ViewObjectContactOfUnoControl::getTemporaryControlForWindow( |
1604 | | const vcl::Window& _rWindow, Reference< XControlContainer >& _inout_ControlContainer, const SdrUnoObj& _rUnoObject ) |
1605 | 0 | { |
1606 | 0 | ControlHolder aControl; |
1607 | |
|
1608 | 0 | InvisibleControlViewAccess aSimulatePageView( _inout_ControlContainer ); |
1609 | 0 | OSL_VERIFY( ViewObjectContactOfUnoControl_Impl::createControlForDevice( aSimulatePageView, *_rWindow.GetOutDev(), _rUnoObject, |
1610 | 0 | _rWindow.GetOutDev()->GetViewTransformation(), _rWindow.GetOutDev()->GetInverseViewTransformation(), aControl ) ); |
1611 | 0 | return aControl.getControl(); |
1612 | 0 | } |
1613 | | |
1614 | | |
1615 | | void ViewObjectContactOfUnoControl::ensureControlVisibility( bool _bVisible ) const |
1616 | 0 | { |
1617 | 0 | SolarMutexGuard aSolarGuard; |
1618 | |
|
1619 | 0 | try |
1620 | 0 | { |
1621 | 0 | const ControlHolder& rControl( m_pImpl->getExistentControl() ); |
1622 | 0 | if ( !rControl.is() ) |
1623 | 0 | return; |
1624 | | |
1625 | | // only need to care for alive mode |
1626 | 0 | if ( rControl.isDesignMode() ) |
1627 | 0 | return; |
1628 | | |
1629 | | // is the visibility correct? |
1630 | 0 | if ( m_pImpl->isControlVisible() == _bVisible ) |
1631 | 0 | return; |
1632 | | |
1633 | | // no -> adjust it |
1634 | 0 | rControl.setVisible( _bVisible ); |
1635 | 0 | DBG_ASSERT( m_pImpl->isControlVisible() == _bVisible, "ViewObjectContactOfUnoControl::ensureControlVisibility: this didn't work!" ); |
1636 | | // now this would mean that either isControlVisible is not reliable, |
1637 | | // or that showing/hiding the window did not work as intended. |
1638 | 0 | } |
1639 | 0 | catch( const Exception& ) |
1640 | 0 | { |
1641 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1642 | 0 | } |
1643 | 0 | } |
1644 | | |
1645 | | |
1646 | | void ViewObjectContactOfUnoControl::setControlDesignMode( bool _bDesignMode ) const |
1647 | 0 | { |
1648 | 0 | SolarMutexGuard aSolarGuard; |
1649 | 0 | m_pImpl->setControlDesignMode( _bDesignMode ); |
1650 | |
|
1651 | 0 | if(!_bDesignMode) |
1652 | 0 | { |
1653 | | // when live mode is switched on, a refresh is needed. The edit mode visualisation |
1654 | | // needs to be repainted and the now used VCL-Window needs to be positioned and |
1655 | | // sized. Both is done from the repaint refresh. |
1656 | 0 | const_cast< ViewObjectContactOfUnoControl* >(this)->ActionChanged(); |
1657 | 0 | } |
1658 | 0 | } |
1659 | | |
1660 | | |
1661 | | void ViewObjectContactOfUnoControl::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const |
1662 | 0 | { |
1663 | 0 | if ( m_pImpl->isDisposed() ) |
1664 | | // our control already died. |
1665 | | // TODO: Is it worth re-creating the control? Finally, this is a pathological situation, it means some instance |
1666 | | // disposed the control though it doesn't own it. So, /me thinks we should not bother here. |
1667 | 0 | return; |
1668 | | |
1669 | 0 | if ( GetObjectContact().getViewInformation2D().getViewTransformation().isIdentity() ) |
1670 | | // remove this when #i115754# is fixed |
1671 | 0 | return; |
1672 | | |
1673 | | // ignore existing controls which are in alive mode and manually switched to "invisible" #i102090# |
1674 | 0 | const ControlHolder& rControl( m_pImpl->getExistentControl() ); |
1675 | 0 | if ( rControl.is() && !rControl.isDesignMode() && !rControl.isVisible() ) |
1676 | 0 | return; |
1677 | | |
1678 | 0 | rVisitor.visit( new LazyControlCreationPrimitive2D( m_pImpl ) ); |
1679 | 0 | } |
1680 | | |
1681 | | |
1682 | | bool ViewObjectContactOfUnoControl::isPrimitiveVisible( const DisplayInfo& _rDisplayInfo ) const |
1683 | 0 | { |
1684 | 0 | SolarMutexGuard aSolarGuard; |
1685 | |
|
1686 | 0 | if ( m_pImpl->hasControl() ) |
1687 | 0 | { |
1688 | 0 | const ::drawinglayer::geometry::ViewInformation2D& rViewInformation( GetObjectContact().getViewInformation2D() ); |
1689 | | #if OSL_DEBUG_LEVEL > 0 |
1690 | | ::basegfx::B2DVector aScale, aTranslate; |
1691 | | double fRotate, fShearX; |
1692 | | rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX ); |
1693 | | #endif |
1694 | |
|
1695 | 0 | if ( !rViewInformation.getViewport().isEmpty() ) |
1696 | 0 | { |
1697 | | // tdf#121963 check and eventually pre-multiply ViewTransformation |
1698 | | // with GridOffset transformation to avoid alternating positions of |
1699 | | // FormControls which are victims of the non-linear calc ViewTransformation |
1700 | | // aka GridOffset. For other paths (e.g. repaint) this is included already |
1701 | | // as part of the object's sequence of B2DPrimitive - representation |
1702 | | // (see ViewObjectContact::getPrimitive2DSequence and how getGridOffset is used there) |
1703 | 0 | basegfx::B2DHomMatrix aViewTransformation(rViewInformation.getObjectToViewTransformation()); |
1704 | |
|
1705 | 0 | if(GetObjectContact().supportsGridOffsets()) |
1706 | 0 | { |
1707 | 0 | const basegfx::B2DVector& rGridOffset(getGridOffset()); |
1708 | |
|
1709 | 0 | if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY()) |
1710 | 0 | { |
1711 | | // pre-multiply: GridOffset needs to be applied directly to logic model data |
1712 | | // of object coordinates, so multiply GridOffset from right to make it |
1713 | | // work as 1st change - these objects may still be part of groups/hierarchies |
1714 | 0 | aViewTransformation = aViewTransformation * basegfx::utils::createTranslateB2DHomMatrix(rGridOffset); |
1715 | 0 | } |
1716 | 0 | } |
1717 | |
|
1718 | 0 | m_pImpl->positionAndZoomControl(aViewTransformation); |
1719 | 0 | } |
1720 | 0 | } |
1721 | |
|
1722 | 0 | return ViewObjectContactOfSdrObj::isPrimitiveVisible( _rDisplayInfo ); |
1723 | 0 | } |
1724 | | |
1725 | | |
1726 | | void ViewObjectContactOfUnoControl::propertyChange() |
1727 | 0 | { |
1728 | 0 | impl_onControlChangedOrModified(); |
1729 | 0 | } |
1730 | | |
1731 | | |
1732 | | void ViewObjectContactOfUnoControl::ActionChanged() |
1733 | 0 | { |
1734 | | // call parent |
1735 | 0 | ViewObjectContactOfSdrObj::ActionChanged(); |
1736 | 0 | const ControlHolder& rControl(m_pImpl->getExistentControl()); |
1737 | |
|
1738 | 0 | if(!rControl.is() || rControl.isDesignMode()) |
1739 | 0 | return; |
1740 | | |
1741 | | // #i93180# if layer visibility has changed and control is in live mode, it is necessary |
1742 | | // to correct visibility to make those control vanish on SdrObject LayerID changes |
1743 | 0 | const SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); |
1744 | |
|
1745 | 0 | if(pSdrPageView) |
1746 | 0 | { |
1747 | 0 | const SdrObject& rObject = getSdrObject(); |
1748 | 0 | const bool bIsLayerVisible( rObject.IsVisible() && pSdrPageView->GetVisibleLayers().IsSet(rObject.GetLayer())); |
1749 | |
|
1750 | 0 | if(rControl.isVisible() != bIsLayerVisible) |
1751 | 0 | { |
1752 | 0 | rControl.setVisible(bIsLayerVisible); |
1753 | 0 | } |
1754 | 0 | } |
1755 | 0 | } |
1756 | | |
1757 | | |
1758 | | void ViewObjectContactOfUnoControl::impl_onControlChangedOrModified() |
1759 | 0 | { |
1760 | | // graphical invalidate at all views |
1761 | 0 | ActionChanged(); |
1762 | | |
1763 | | // #i93318# flush Primitive2DContainer to force recreation with updated XControlModel |
1764 | | // since e.g. background color has changed and existing decompositions are possibly no |
1765 | | // longer valid. Unfortunately this is not detected from ControlPrimitive2D::operator== |
1766 | | // since it only has a uno reference to the XControlModel |
1767 | 0 | flushPrimitive2DSequence(); |
1768 | 0 | } |
1769 | | |
1770 | | UnoControlPrintOrPreviewContact::UnoControlPrintOrPreviewContact( ObjectContactOfPageView& _rObjectContact, ViewContactOfUnoControl& _rViewContact ) |
1771 | 0 | :ViewObjectContactOfUnoControl( _rObjectContact, _rViewContact ) |
1772 | 0 | { |
1773 | 0 | } |
1774 | | |
1775 | | |
1776 | | UnoControlPrintOrPreviewContact::~UnoControlPrintOrPreviewContact() |
1777 | | { |
1778 | | } |
1779 | | |
1780 | | |
1781 | | void UnoControlPrintOrPreviewContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor ) const |
1782 | 0 | { |
1783 | 0 | if ( !m_pImpl->isPrintableControl() ) |
1784 | 0 | return; |
1785 | 0 | ViewObjectContactOfUnoControl::createPrimitive2DSequence( rDisplayInfo, rVisitor ); |
1786 | 0 | } |
1787 | | |
1788 | | |
1789 | | } // namespace sdr::contact |
1790 | | |
1791 | | |
1792 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |