1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
31 #include <com/sun/star/awt/XControl.hpp>
32 #include <com/sun/star/awt/XControlModel.hpp>
33 #include <com/sun/star/awt/XControlContainer.hpp>
34 #include <com/sun/star/awt/XWindow2.hpp>
35 #include <com/sun/star/awt/PosSize.hpp>
36 #include <com/sun/star/awt/XView.hpp>
37 #include <com/sun/star/beans/XPropertySet.hpp>
38 #include <com/sun/star/beans/XPropertySetInfo.hpp>
39 #include <com/sun/star/lang/XComponent.hpp>
40 #include <com/sun/star/awt/InvalidateStyle.hpp>
41 #include <com/sun/star/util/XModeChangeListener.hpp>
42 #include <com/sun/star/util/XModeChangeBroadcaster.hpp>
43 #include <com/sun/star/uno/XComponentContext.hpp>
44 #include <com/sun/star/container/XContainerListener.hpp>
45 #include <com/sun/star/container/XContainer.hpp>
47 #include <vcl/canvastools.hxx>
48 #include <vcl/svapp.hxx>
49 #include <vcl/window.hxx>
50 #include <comphelper/lok.hxx>
51 #include <comphelper/processfactory.hxx>
52 #include <comphelper/scopeguard.hxx>
53 #include <cppuhelper/implbase.hxx>
54 #include <toolkit/helper/vclunohelper.hxx>
55 #include <comphelper/diagnose_ex.hxx>
56 #include <tools/debug.hxx>
57 #include <basegfx/matrix/b2dhommatrix.hxx>
58 #include <basegfx/matrix/b2dhommatrixtools.hxx>
59 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
64 Form controls (more precise: UNO Controls) in the drawing layer are ... prone to breakage, since they have some
65 specialities which the drawing layer currently doesn't capture too well. In particular, having a living VCL
66 window as child of the document window, and coupling this Window to a drawing layer object, makes things
69 Below is a list of issues which existed in the past. Whenever you change code here, you're encouraged to
70 verify those issues are still fixed. (Whenever you have some additional time, you're encouraged to write
71 an automatic test for one or more of those issues for which this is possible :)
73 https://bz.apache.org/ooo/show_bug.cgi?id=105992
74 zooming documents containing (alive) form controls improperly positions the controls
76 https://bz.apache.org/ooo/show_bug.cgi?id=104362
77 crash when copy a control
79 https://bz.apache.org/ooo/show_bug.cgi?id=104544
80 Gridcontrol duplicated after design view on/off
82 https://bz.apache.org/ooo/show_bug.cgi?id=102089
83 print preview shows control elements with property printable=false
85 https://bz.apache.org/ooo/show_bug.cgi?id=102090
86 problem with setVisible on TextControl
88 https://bz.apache.org/ooo/show_bug.cgi?id=103138
89 loop when insert a control in draw
91 https://bz.apache.org/ooo/show_bug.cgi?id=101398
92 initially-displaying a document with many controls is very slow
94 https://bz.apache.org/ooo/show_bug.cgi?id=72429
95 repaint error in form wizard in bugdoc database
97 https://bz.apache.org/ooo/show_bug.cgi?id=72694
98 form control artifacts when scrolling a text fast
103 namespace sdr::contact
{
106 using namespace ::com::sun::star::awt::InvalidateStyle
;
107 using ::com::sun::star::uno::Reference
;
108 using ::com::sun::star::uno::XInterface
;
109 using ::com::sun::star::uno::UNO_QUERY
;
110 using ::com::sun::star::uno::UNO_QUERY_THROW
;
111 using ::com::sun::star::uno::Exception
;
112 using ::com::sun::star::awt::XControl
;
113 using ::com::sun::star::awt::XControlModel
;
114 using ::com::sun::star::awt::XControlContainer
;
115 using ::com::sun::star::awt::XWindow2
;
116 using ::com::sun::star::awt::XWindowListener
;
117 using ::com::sun::star::awt::PosSize::POSSIZE
;
118 using ::com::sun::star::awt::XView
;
119 using ::com::sun::star::awt::WindowEvent
;
120 using ::com::sun::star::beans::XPropertySet
;
121 using ::com::sun::star::beans::XPropertySetInfo
;
122 using ::com::sun::star::lang::XComponent
;
123 using ::com::sun::star::awt::XWindowPeer
;
124 using ::com::sun::star::beans::XPropertyChangeListener
;
125 using ::com::sun::star::util::XModeChangeListener
;
126 using ::com::sun::star::util::XModeChangeBroadcaster
;
127 using ::com::sun::star::util::ModeChangeEvent
;
128 using ::com::sun::star::lang::EventObject
;
129 using ::com::sun::star::beans::PropertyChangeEvent
;
130 using ::com::sun::star::container::XContainerListener
;
131 using ::com::sun::star::container::XContainer
;
132 using ::com::sun::star::container::ContainerEvent
;
133 using ::com::sun::star::uno::Any
;
140 Reference
< XControl
> m_xControl
;
141 Reference
< XWindow2
> m_xControlWindow
;
142 Reference
< XView
> m_xControlView
;
149 explicit ControlHolder( const Reference
< XControl
>& _rxControl
)
154 ControlHolder
& operator=( const Reference
< XControl
>& _rxControl
)
158 m_xControl
= _rxControl
;
159 if ( m_xControl
.is() )
161 m_xControlWindow
.set( m_xControl
, UNO_QUERY
);
162 m_xControlView
.set( m_xControl
, UNO_QUERY
);
163 if ( !m_xControlWindow
.is() || !m_xControlView
.is() )
165 OSL_FAIL( "ControlHolder::operator=: invalid XControl, missing required interfaces!" );
174 bool is() const { return m_xControl
.is() && m_xControlWindow
.is() && m_xControlView
.is(); }
175 void clear() { m_xControl
.clear(); m_xControlWindow
.clear(); m_xControlView
.clear(); }
177 // delegators for the methods of the UNO interfaces
178 // Note all those will crash if called for a NULL object.
179 bool isDesignMode() const { return m_xControl
->isDesignMode(); }
180 void setDesignMode( const bool _bDesign
) const { m_xControl
->setDesignMode( _bDesign
); }
181 bool isVisible() const { return m_xControlWindow
->isVisible(); }
182 void setVisible( const bool _bVisible
) const { m_xControlWindow
->setVisible( _bVisible
); }
183 Reference
< XControlModel
>
184 getModel() const { return m_xControl
->getModel(); }
185 void setModel( const Reference
< XControlModel
>& _m
) const { m_xControl
->setModel( _m
); }
187 void addWindowListener( const Reference
< XWindowListener
>& _l
) const { m_xControlWindow
->addWindowListener( _l
); }
188 void removeWindowListener( const Reference
< XWindowListener
>& _l
) const { m_xControlWindow
->removeWindowListener( _l
); }
189 void setPosSize( const tools::Rectangle
& _rPosSize
) const;
192 void setZoom( const ::basegfx::B2DVector
& _rScale
) const;
196 void invalidate() const;
199 const Reference
< XControl
>& getControl() const { return m_xControl
; }
202 bool operator==( const ControlHolder
& _rControl
, const Reference
< XInterface
>& _rxCompare
)
204 return _rControl
.getControl() == _rxCompare
;
207 bool operator==( const ControlHolder
& _rControl
, const Any
& _rxCompare
)
209 return _rControl
== Reference
< XInterface
>( _rxCompare
, UNO_QUERY
);
214 void ControlHolder::setPosSize( const tools::Rectangle
& _rPosSize
) const
216 // no check whether we're valid, this is the responsibility of the caller
218 // don't call setPosSize when pos/size did not change #i104181#
219 ::tools::Rectangle
aCurrentRect( getPosSize() );
220 if ( aCurrentRect
!= _rPosSize
)
222 m_xControlWindow
->setPosSize(
223 _rPosSize
.Left(), _rPosSize
.Top(), _rPosSize
.GetWidth(), _rPosSize
.GetHeight(),
230 ::tools::Rectangle
ControlHolder::getPosSize() const
232 // no check whether we're valid, this is the responsibility of the caller
233 return VCLUnoHelper::ConvertToVCLRect( m_xControlWindow
->getPosSize() );
237 void ControlHolder::setZoom( const ::basegfx::B2DVector
& _rScale
) const
239 // no check whether we're valid, this is the responsibility of the caller
240 m_xControlView
->setZoom( static_cast<float>(_rScale
.getX()), static_cast<float>(_rScale
.getY()) );
244 void ControlHolder::invalidate() const
246 Reference
< XWindowPeer
> xPeer( m_xControl
->getPeer() );
249 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow( xPeer
);
250 OSL_ENSURE( pWindow
, "ControlHolder::invalidate: no implementation access!" );
252 pWindow
->Invalidate();
257 ::basegfx::B2DVector
ControlHolder::getZoom() const
259 // no check whether we're valid, this is the responsibility of the caller
261 // Argh. Why does XView have a setZoom only, but not a getZoom?
262 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow( m_xControl
->getPeer() );
263 OSL_ENSURE( pWindow
, "ControlHolder::getZoom: no implementation access!" );
265 ::basegfx::B2DVector
aZoom( 1, 1 );
268 const Fraction
& rZoom( pWindow
->GetZoom() );
269 aZoom
.setX( static_cast<double>(rZoom
) );
270 aZoom
.setY( static_cast<double>(rZoom
) );
275 namespace UnoControlContactHelper
{
277 /** positions a control, and sets its zoom mode, using a given transformation and output device
279 static void adjustControlGeometry_throw( const ControlHolder
& _rControl
, const tools::Rectangle
& _rLogicBoundingRect
,
280 const basegfx::B2DHomMatrix
& _rViewTransformation
, const ::basegfx::B2DHomMatrix
& _rZoomLevelNormalization
)
282 // In the LOK case, control geometry is handled by LokControlHandler
283 if (comphelper::LibreOfficeKit::isActive())
286 OSL_PRECOND( _rControl
.is(), "UnoControlContactHelper::adjustControlGeometry_throw: illegal control!" );
287 if ( !_rControl
.is() )
290 #if OSL_DEBUG_LEVEL > 0
291 ::basegfx::B2DTuple aViewScale
, aViewTranslate
;
292 double nViewRotate(0), nViewShearX(0);
293 _rViewTransformation
.decompose( aViewScale
, aViewTranslate
, nViewRotate
, nViewShearX
);
295 ::basegfx::B2DTuple aZoomScale
, aZoomTranslate
;
296 double nZoomRotate(0), nZoomShearX(0);
297 _rZoomLevelNormalization
.decompose( aZoomScale
, aZoomTranslate
, nZoomRotate
, nZoomShearX
);
300 // transform the logic bound rect, using the view transformation, to pixel coordinates
301 ::basegfx::B2DPoint
aTopLeft( _rLogicBoundingRect
.Left(), _rLogicBoundingRect
.Top() );
302 aTopLeft
*= _rViewTransformation
;
303 ::basegfx::B2DPoint
aBottomRight( _rLogicBoundingRect
.Right(), _rLogicBoundingRect
.Bottom() );
304 aBottomRight
*= _rViewTransformation
;
306 const tools::Rectangle
aPaintRectPixel(static_cast<tools::Long
>(std::round(aTopLeft
.getX())),
307 static_cast<tools::Long
>(std::round(aTopLeft
.getY())),
308 static_cast<tools::Long
>(std::round(aBottomRight
.getX())),
309 static_cast<tools::Long
>(std::round(aBottomRight
.getY())));
310 _rControl
.setPosSize( aPaintRectPixel
);
312 // determine the scale from the current view transformation, and the normalization matrix
313 ::basegfx::B2DHomMatrix
aObtainResolutionDependentScale( _rViewTransformation
* _rZoomLevelNormalization
);
314 ::basegfx::B2DVector aScale
, aTranslate
;
315 double fRotate
, fShearX
;
316 aObtainResolutionDependentScale
.decompose( aScale
, aTranslate
, fRotate
, fShearX
);
317 _rControl
.setZoom( aScale
);
320 /** disposes the given control
322 static void disposeAndClearControl_nothrow( ControlHolder
& _rControl
)
326 Reference
< XComponent
> xControlComp
= _rControl
.getControl();
327 if ( xControlComp
.is() )
328 xControlComp
->dispose();
330 catch( const Exception
& )
332 DBG_UNHANDLED_EXCEPTION("svx");
341 /** interface encapsulating access to an SdrPageView, stripped down to the methods we really need
343 class IPageViewAccess
346 /** determines whether the view is currently in design mode
348 virtual bool isDesignMode() const = 0;
350 /** retrieves the control container for a given output device
352 virtual Reference
< XControlContainer
>
353 getControlContainer( const OutputDevice
& _rDevice
) const = 0;
355 /** determines whether a given layer is visible
357 virtual bool isLayerVisible( SdrLayerID _nLayerID
) const = 0;
360 ~IPageViewAccess() {}
363 /** is a ->IPageViewAccess implementation based on a real ->SdrPageView instance
365 class SdrPageViewAccess
: public IPageViewAccess
367 const SdrPageView
& m_rPageView
;
369 explicit SdrPageViewAccess( const SdrPageView
& _rPageView
) : m_rPageView( _rPageView
) { }
371 virtual ~SdrPageViewAccess() {}
373 virtual bool isDesignMode() const override
;
374 virtual Reference
< XControlContainer
>
375 getControlContainer( const OutputDevice
& _rDevice
) const override
;
376 virtual bool isLayerVisible( SdrLayerID _nLayerID
) const override
;
381 bool SdrPageViewAccess::isDesignMode() const
383 return m_rPageView
.GetView().IsDesignMode();
387 Reference
< XControlContainer
> SdrPageViewAccess::getControlContainer( const OutputDevice
& _rDevice
) const
389 Reference
< XControlContainer
> xControlContainer
= m_rPageView
.GetControlContainer( _rDevice
);
390 DBG_ASSERT( xControlContainer
.is() || nullptr == m_rPageView
.FindPageWindow( _rDevice
),
391 "SdrPageViewAccess::getControlContainer: the output device is known, but there is no control container for it?" );
392 return xControlContainer
;
396 bool SdrPageViewAccess::isLayerVisible( SdrLayerID _nLayerID
) const
398 return m_rPageView
.GetVisibleLayers().IsSet( _nLayerID
);
403 /** is a ->IPageViewAccess implementation which can be used to create an invisible control for
406 class InvisibleControlViewAccess
: public IPageViewAccess
409 Reference
< XControlContainer
>& m_rControlContainer
;
411 explicit InvisibleControlViewAccess( Reference
< XControlContainer
>& _inout_ControlContainer
)
412 :m_rControlContainer( _inout_ControlContainer
)
416 virtual ~InvisibleControlViewAccess() {}
418 virtual bool isDesignMode() const override
;
419 virtual Reference
< XControlContainer
>
420 getControlContainer( const OutputDevice
& _rDevice
) const override
;
421 virtual bool isLayerVisible( SdrLayerID _nLayerID
) const override
;
426 bool InvisibleControlViewAccess::isDesignMode() const
432 Reference
< XControlContainer
> InvisibleControlViewAccess::getControlContainer( const OutputDevice
& _rDevice
) const
434 if ( !m_rControlContainer
.is() )
436 const vcl::Window
* pWindow
= _rDevice
.GetOwnerWindow();
437 OSL_ENSURE( pWindow
, "InvisibleControlViewAccess::getControlContainer: expected to be called for a window only!" );
439 m_rControlContainer
= VCLUnoHelper::CreateControlContainer( const_cast< vcl::Window
* >( pWindow
) );
441 return m_rControlContainer
;
445 bool InvisibleControlViewAccess::isLayerVisible( SdrLayerID
/*_nLayerID*/ ) const
452 //= DummyPageViewAccess
454 /** is a ->IPageViewAccess implementation which can be used to create a control for an arbitrary
457 The implementation will report the "PageView" as being in design mode, all layers to be visible,
458 and will not return any ControlContainer, so all control container related features (notifications etc)
461 class DummyPageViewAccess
: public IPageViewAccess
464 DummyPageViewAccess()
468 virtual ~DummyPageViewAccess() {}
470 virtual bool isDesignMode() const override
;
471 virtual Reference
< XControlContainer
>
472 getControlContainer( const OutputDevice
& _rDevice
) const override
;
473 virtual bool isLayerVisible( SdrLayerID _nLayerID
) const override
;
478 bool DummyPageViewAccess::isDesignMode() const
484 Reference
< XControlContainer
> DummyPageViewAccess::getControlContainer( const OutputDevice
& /*_rDevice*/ ) const
490 bool DummyPageViewAccess::isLayerVisible( SdrLayerID
/*_nLayerID*/ ) const
496 //= ViewObjectContactOfUnoControl_Impl
498 typedef ::cppu::WeakImplHelper
< XWindowListener
499 , XPropertyChangeListener
501 , XModeChangeListener
502 > ViewObjectContactOfUnoControl_Impl_Base
;
504 class ViewObjectContactOfUnoControl_Impl
:
505 public ViewObjectContactOfUnoControl_Impl_Base
508 // tdf#41935 note that access to members is protected with SolarMutex;
509 // the class previously had its own mutex but that is prone to deadlock
511 /// the instance whose IMPL we are
512 ViewObjectContactOfUnoControl
* m_pAntiImpl
;
514 /// are we currently inside impl_ensureControl_nothrow?
515 bool m_bCreatingControl
;
517 /// the control we're responsible for
518 ControlHolder m_aControl
;
520 /// the ControlContainer where we inserted our control
521 Reference
< XContainer
> m_xContainer
;
523 /// the output device for which the control was created
524 VclPtr
<OutputDevice
> m_pOutputDeviceForWindow
;
526 /// flag indicating whether the control is currently visible
527 bool m_bControlIsVisible
;
529 /// are we currently listening at a design mode control?
530 bool m_bIsDesignModeListening
;
538 /// is the control currently in design mode?
539 mutable ViewControlMode m_eControlDesignMode
;
541 ::basegfx::B2DHomMatrix m_aZoomLevelNormalization
;
544 explicit ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl
* _pAntiImpl
);
545 ViewObjectContactOfUnoControl_Impl(const ViewObjectContactOfUnoControl_Impl
&) = delete;
546 ViewObjectContactOfUnoControl_Impl
& operator=(const ViewObjectContactOfUnoControl_Impl
&) = delete;
548 /** disposes the instance, which is nonfunctional afterwards
552 /** determines whether the instance is disposed
554 bool isDisposed() const { return impl_isDisposed_nofail(); }
556 /** returns the SdrUnoObject associated with the ViewContact
561 SdrUnoObj
* getUnoObject() const;
563 /** ensures that we have an ->XControl
565 Must only be called if a control is needed when no DisplayInfo is present, yet.
567 For creating a control, an ->OutputDevice is needed, and an ->SdrPageView. Both will be obtained
568 from a ->ObjectContactOfPageView. So, if our (anti-impl's) object contact is not a ->ObjectContactOfPageView,
569 this method fill fail.
571 Failure of this method will be reported via an assertion in a non-product version.
573 void ensureControl( const basegfx::B2DHomMatrix
* _pInitialViewTransformationOrNULL
);
575 /** returns our XControl, if it already has been created
577 If you want to ensure that the control exists before accessing it, use ->ensureControl
580 getExistentControl() const { return m_aControl
; }
583 hasControl() const { return m_aControl
.is(); }
585 /** positions our XControl according to the geometry settings in the SdrUnoObj, modified by the given
586 transformation, and sets proper zoom settings according to our device
589 ->m_pOutputDeviceForWindow and ->m_aControl are not <NULL/>
591 void positionAndZoomControl( const basegfx::B2DHomMatrix
& _rViewTransformation
) const;
593 /** determines whether or not our control is printable
595 Effectively, this method returns the value of the "Printable" property
596 of the control's model. If we have no control, <FALSE/> is returned.
598 bool isPrintableControl() const;
600 /** sets the design mode on the control, or at least remembers the flag for the
601 time the control is created
603 void setControlDesignMode( bool _bDesignMode
) const;
605 /** determines whether our control is currently visible
608 bool isControlVisible() const { return m_bControlIsVisible
; }
610 /// creates an XControl for the given device and SdrUnoObj
612 createControlForDevice(
613 IPageViewAccess
const & _rPageView
,
614 const OutputDevice
& _rDevice
,
615 const SdrUnoObj
& _rUnoObject
,
616 const basegfx::B2DHomMatrix
& _rInitialViewTransformation
,
617 const basegfx::B2DHomMatrix
& _rInitialZoomNormalization
,
618 ControlHolder
& _out_rControl
621 const ViewContactOfUnoControl
&
622 getViewContact() const
624 ENSURE_OR_THROW( !impl_isDisposed_nofail(), "already disposed" );
625 return static_cast< const ViewContactOfUnoControl
& >( m_pAntiImpl
->GetViewContact() );
629 virtual ~ViewObjectContactOfUnoControl_Impl() override
;
632 virtual void SAL_CALL
disposing( const EventObject
& Source
) override
;
635 virtual void SAL_CALL
windowResized( const WindowEvent
& e
) override
;
636 virtual void SAL_CALL
windowMoved( const WindowEvent
& e
) override
;
637 virtual void SAL_CALL
windowShown( const EventObject
& e
) override
;
638 virtual void SAL_CALL
windowHidden( const EventObject
& e
) override
;
640 // XPropertyChangeListener
641 virtual void SAL_CALL
propertyChange( const PropertyChangeEvent
& evt
) override
;
643 // XModeChangeListener
644 virtual void SAL_CALL
modeChanged( const ModeChangeEvent
& _rSource
) override
;
646 // XContainerListener
647 virtual void SAL_CALL
elementInserted( const css::container::ContainerEvent
& Event
) override
;
648 virtual void SAL_CALL
elementRemoved( const css::container::ContainerEvent
& Event
) override
;
649 virtual void SAL_CALL
elementReplaced( const css::container::ContainerEvent
& Event
) override
;
652 /** retrieves the SdrPageView which our associated SdrPageViewWindow belongs to
654 @param out_rpPageView
655 a reference to a pointer holding, upon return, the desired SdrPageView
658 <TRUE/> if and only if a ->SdrPageView could be obtained
661 We really belong to an SdrPageViewWindow. Perhaps (I'm not sure ATM :)
662 there are instance for which this might not be true, but those instances
663 should never have a need to call this method.
669 The method expects success, if it returns with <FALSE/>, this will have been
674 bool impl_getPageView_nothrow( SdrPageView
*& _out_rpPageView
);
676 /** adjusts the control visibility so it respects its layer's visibility
679 ->m_aControl is not <NULL/>
685 We really belong to an SdrPageViewWindow. There are instance for which this
686 might not be true, but those instances should never have a need to call
689 void impl_adjustControlVisibilityToLayerVisibility_throw();
691 /** adjusts the control visibility so it respects its layer's visibility
693 The control must never be visible if it's in design mode.
694 In alive mode, it must be visibility if and only it's on a visible layer.
697 the control whose visibility is to be adjusted
700 provides access to the attributes of the SdrPageView which the control finally belongs to
705 @param _bIsCurrentlyVisible
706 determines whether the control is currently visible. Note that this is only a shortcut for
707 querying _rxControl for the XWindow2 interface, and calling isVisible at this interface.
708 This shortcut has been chosen since the caller usually already has this information.
709 If _bForce is <TRUE/>, _bIsCurrentlyVisible is ignored.
712 set to <TRUE/> if you want to force a ->XWindow::setVisible call,
713 no matter if the control visibility is already correct
718 static void impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder
& _rxControl
, const SdrUnoObj
& _rUnoObject
,
719 IPageViewAccess
const & _rPageView
, bool _bIsCurrentlyVisible
, bool _bForce
);
721 /** starts or stops listening at various aspects of our control
724 ->m_aControl is not <NULL/>
726 void impl_switchControlListening_nothrow( bool _bStart
);
728 /** starts or stops listening at our control container
731 ->m_xContainer is not <NULL/>
733 void impl_switchContainerListening_nothrow( bool _bStart
);
735 /** starts or stops listening at the control for design-mode relevant facets
737 void impl_switchDesignModeListening_nothrow( bool _bStart
);
739 /** starts or stops listening for all properties at our control
742 determines whether to start or to stop listening
745 ->m_aControl is not <NULL/>
747 void impl_switchPropertyListening_nothrow( bool _bStart
);
749 /** disposes the instance
750 @param _bAlsoDisposeControl
751 determines whether the XControl should be disposed, too
753 void impl_dispose_nothrow( bool _bAlsoDisposeControl
);
755 /** determines whether the instance is disposed
758 bool impl_isDisposed_nofail() const { return m_pAntiImpl
== nullptr; }
760 /** determines whether the control currently is in design mode
763 The design mode must already be known. It is known when we first had access to
764 an SdrPageView (which carries this flag), or somebody explicitly set it from
767 bool impl_isControlDesignMode_nothrow() const
769 DBG_ASSERT( m_eControlDesignMode
!= eUnknown
, "ViewObjectContactOfUnoControl_Impl::impl_isControlDesignMode_nothrow: mode is still unknown!" );
770 return m_eControlDesignMode
== eDesign
;
773 /** ensures that we have a control for the given PageView/OutputDevice
775 bool impl_ensureControl_nothrow(
776 IPageViewAccess
const & _rPageView
,
777 const OutputDevice
& _rDevice
,
778 const basegfx::B2DHomMatrix
& _rInitialViewTransformation
781 const OutputDevice
& impl_getOutputDevice_throw() const;
786 class LazyControlCreationPrimitive2D
: public ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D
789 typedef ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D BufferedDecompositionPrimitive2D
;
794 ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
,
795 const ::drawinglayer::geometry::ViewInformation2D
& rViewInformation
798 virtual void create2DDecomposition(
799 ::drawinglayer::primitive2d::Primitive2DContainer
& rContainer
,
800 const ::drawinglayer::geometry::ViewInformation2D
& rViewInformation
803 virtual ::basegfx::B2DRange
805 const ::drawinglayer::geometry::ViewInformation2D
& rViewInformation
809 explicit LazyControlCreationPrimitive2D( ::rtl::Reference
< ViewObjectContactOfUnoControl_Impl
> _pVOCImpl
)
810 :m_pVOCImpl(std::move( _pVOCImpl
))
812 ENSURE_OR_THROW( m_pVOCImpl
.is(), "Illegal argument." );
813 getTransformation( m_pVOCImpl
->getViewContact(), m_aTransformation
);
816 virtual bool operator==(const BasePrimitive2D
& rPrimitive
) const override
;
818 // declare unique ID for this primitive class
819 virtual sal_uInt32
getPrimitive2DID() const override
;
821 static void getTransformation( const ViewContactOfUnoControl
& _rVOC
, ::basegfx::B2DHomMatrix
& _out_Transformation
);
824 void impl_positionAndZoomControl( const ::drawinglayer::geometry::ViewInformation2D
& _rViewInformation
) const
826 if ( !_rViewInformation
.getViewport().isEmpty() )
827 m_pVOCImpl
->positionAndZoomControl( _rViewInformation
.getObjectToViewTransformation() );
831 ::rtl::Reference
< ViewObjectContactOfUnoControl_Impl
> m_pVOCImpl
;
832 /** The geometry is part of the identity of a primitive, so we cannot calculate it on demand
833 (since the data the calculation is based on might have changed then), but need to calc
834 it at construction time, and remember it.
836 ::basegfx::B2DHomMatrix m_aTransformation
;
841 ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl
* _pAntiImpl
)
842 :m_pAntiImpl( _pAntiImpl
)
843 ,m_bCreatingControl( false )
844 ,m_pOutputDeviceForWindow( nullptr )
845 ,m_bControlIsVisible( false )
846 ,m_bIsDesignModeListening( false )
847 ,m_eControlDesignMode( eUnknown
)
849 DBG_ASSERT( m_pAntiImpl
, "ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl: invalid AntiImpl!" );
851 const OutputDevice
& rPageViewDevice( impl_getOutputDevice_throw() );
852 m_aZoomLevelNormalization
= rPageViewDevice
.GetInverseViewTransformation();
854 #if OSL_DEBUG_LEVEL > 0
855 ::basegfx::B2DVector aScale
, aTranslate
;
856 double fRotate
, fShearX
;
857 m_aZoomLevelNormalization
.decompose( aScale
, aTranslate
, fRotate
, fShearX
);
860 ::basegfx::B2DHomMatrix aScaleNormalization
;
861 const MapMode
& aCurrentDeviceMapMode( rPageViewDevice
.GetMapMode() );
862 aScaleNormalization
.set( 0, 0, static_cast<double>(aCurrentDeviceMapMode
.GetScaleX()) );
863 aScaleNormalization
.set( 1, 1, static_cast<double>(aCurrentDeviceMapMode
.GetScaleY()) );
864 m_aZoomLevelNormalization
*= aScaleNormalization
;
866 #if OSL_DEBUG_LEVEL > 0
867 m_aZoomLevelNormalization
.decompose( aScale
, aTranslate
, fRotate
, fShearX
);
872 ViewObjectContactOfUnoControl_Impl::~ViewObjectContactOfUnoControl_Impl()
874 if ( !impl_isDisposed_nofail() )
883 void ViewObjectContactOfUnoControl_Impl::impl_dispose_nothrow( bool _bAlsoDisposeControl
)
885 if ( impl_isDisposed_nofail() )
888 if ( m_aControl
.is() )
889 impl_switchControlListening_nothrow( false );
891 if ( m_xContainer
.is() )
892 impl_switchContainerListening_nothrow( false );
894 // dispose the control
895 if ( _bAlsoDisposeControl
)
896 UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl
);
899 m_xContainer
.clear();
900 m_pOutputDeviceForWindow
= nullptr;
901 m_bControlIsVisible
= false;
903 m_pAntiImpl
= nullptr;
907 void ViewObjectContactOfUnoControl_Impl::dispose()
909 SolarMutexGuard aSolarGuard
;
910 impl_dispose_nothrow( true );
914 SdrUnoObj
* ViewObjectContactOfUnoControl_Impl::getUnoObject() const
916 OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::getUnoObject: already disposed()" );
917 if ( impl_isDisposed_nofail() )
919 auto pRet
= dynamic_cast< SdrUnoObj
* >( m_pAntiImpl
->GetViewContact().TryToGetSdrObject() );
920 DBG_ASSERT( pRet
|| !m_pAntiImpl
->GetViewContact().TryToGetSdrObject(),
921 "ViewObjectContactOfUnoControl_Impl::getUnoObject: invalid SdrObject!" );
926 void ViewObjectContactOfUnoControl_Impl::positionAndZoomControl( const basegfx::B2DHomMatrix
& _rViewTransformation
) const
928 OSL_PRECOND( m_aControl
.is(), "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no output device or no control!" );
929 if ( !m_aControl
.is() )
934 SdrUnoObj
* pUnoObject
= getUnoObject();
937 const tools::Rectangle
aRect( pUnoObject
->GetLogicRect() );
938 UnoControlContactHelper::adjustControlGeometry_throw( m_aControl
, aRect
, _rViewTransformation
, m_aZoomLevelNormalization
);
941 OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no SdrUnoObj!" );
943 catch( const Exception
& )
945 DBG_UNHANDLED_EXCEPTION("svx");
950 void ViewObjectContactOfUnoControl_Impl::ensureControl( const basegfx::B2DHomMatrix
* _pInitialViewTransformationOrNULL
)
952 OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::ensureControl: already disposed()" );
953 if ( impl_isDisposed_nofail() )
956 ObjectContactOfPageView
* pPageViewContact
= dynamic_cast< ObjectContactOfPageView
* >( &m_pAntiImpl
->GetObjectContact() );
957 if ( pPageViewContact
)
959 SdrPageViewAccess
aPVAccess( pPageViewContact
->GetPageWindow().GetPageView() );
960 const OutputDevice
& rDevice( *m_pAntiImpl
->getPageViewOutputDevice() );
961 impl_ensureControl_nothrow(
964 _pInitialViewTransformationOrNULL
? *_pInitialViewTransformationOrNULL
: rDevice
.GetViewTransformation()
969 DummyPageViewAccess aNoPageView
;
970 const OutputDevice
& rDevice( impl_getOutputDevice_throw() );
971 impl_ensureControl_nothrow(
974 _pInitialViewTransformationOrNULL
? *_pInitialViewTransformationOrNULL
: rDevice
.GetViewTransformation()
979 const OutputDevice
& ViewObjectContactOfUnoControl_Impl::impl_getOutputDevice_throw() const
981 // do not use ObjectContact::TryToGetOutputDevice, it would not care for the PageWindow's
982 // OriginalPaintWindow
983 const OutputDevice
* oPageOutputDev
= m_pAntiImpl
->getPageViewOutputDevice();
985 return *oPageOutputDev
;
987 const OutputDevice
* pDevice
= m_pAntiImpl
->GetObjectContact().TryToGetOutputDevice();
988 ENSURE_OR_THROW( pDevice
, "no output device -> no control" );
995 void lcl_resetFlag( bool& rbFlag
)
1002 bool ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow( IPageViewAccess
const & _rPageView
, const OutputDevice
& _rDevice
,
1003 const basegfx::B2DHomMatrix
& _rInitialViewTransformation
)
1005 if ( m_bCreatingControl
)
1007 OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: reentrance is not really good here!" );
1008 // We once had a situation where this was called reentrantly, which lead to all kind of strange effects. All
1009 // those affected the grid control, which is the only control so far which is visible in design mode (and
1010 // not only in alive mode).
1011 // Creating the control triggered a Window::Update on some of its child windows, which triggered a
1012 // Paint on parent of the grid control (e.g. the SwEditWin), which triggered a reentrant call to this method,
1013 // which it is not really prepared for.
1015 // /me thinks that re-entrance should be caught on a higher level, i.e. the Drawing Layer should not allow
1016 // reentrant paint requests. For the moment, until /me can discuss this with AW, catch it here. #i104544#
1020 m_bCreatingControl
= true;
1021 ::comphelper::ScopeGuard
aGuard([&] () { lcl_resetFlag(m_bCreatingControl
); });
1023 if ( m_aControl
.is() )
1025 if ( m_pOutputDeviceForWindow
.get() == &_rDevice
)
1028 // Somebody requested a control for a new device, which means either of
1029 // - our PageView's paint window changed since we were last here
1030 // - we don't belong to a page view, and are simply painted onto different devices
1031 // The first sounds strange (doesn't it?), the second means we could perhaps
1032 // optimize this in the future - there is no need to re-create the control every time,
1034 if ( m_xContainer
.is() )
1035 impl_switchContainerListening_nothrow( false );
1036 impl_switchControlListening_nothrow( false );
1037 UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl
);
1040 SdrUnoObj
* pUnoObject
= getUnoObject();
1044 ControlHolder aControl
;
1045 if ( !createControlForDevice( _rPageView
, _rDevice
, *pUnoObject
, _rInitialViewTransformation
, m_aZoomLevelNormalization
, aControl
) )
1048 m_pOutputDeviceForWindow
= const_cast< OutputDevice
* >( &_rDevice
);
1049 m_aControl
= aControl
;
1050 m_xContainer
.set(_rPageView
.getControlContainer( _rDevice
), css::uno::UNO_QUERY
);
1051 DBG_ASSERT( ( m_xContainer
.is() // either have a XControlContainer
1052 || ( ( !_rPageView
.getControlContainer( _rDevice
).is() ) // or don't have any container,
1053 && ( _rDevice
.GetOwnerWindow() == nullptr ) // which is allowed for non-Window instances only
1056 "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: no XContainer at the ControlContainer!" );
1060 m_eControlDesignMode
= m_aControl
.isDesignMode() ? eDesign
: eAlive
;
1061 m_bControlIsVisible
= m_aControl
.isVisible();
1063 catch( const Exception
& )
1065 DBG_UNHANDLED_EXCEPTION("svx");
1068 // start listening at all aspects of the control which are interesting to us ...
1069 impl_switchControlListening_nothrow( true );
1071 // start listening at the control container, in case somebody tampers with our control
1072 if ( m_xContainer
.is() )
1073 impl_switchContainerListening_nothrow( true );
1075 return m_aControl
.is();
1079 bool ViewObjectContactOfUnoControl_Impl::createControlForDevice( IPageViewAccess
const & _rPageView
,
1080 const OutputDevice
& _rDevice
, const SdrUnoObj
& _rUnoObject
, const basegfx::B2DHomMatrix
& _rInitialViewTransformation
,
1081 const basegfx::B2DHomMatrix
& _rInitialZoomNormalization
, ControlHolder
& _out_rControl
)
1083 _out_rControl
.clear();
1085 const Reference
< XControlModel
>& xControlModel( _rUnoObject
.GetUnoControlModel() );
1086 DBG_ASSERT( xControlModel
.is(), "ViewObjectContactOfUnoControl_Impl::createControlForDevice: no control model at the SdrUnoObject!?" );
1087 if ( !xControlModel
.is() )
1090 bool bSuccess
= false;
1093 const OUString
& sControlServiceName( _rUnoObject
.GetUnoControlTypeName() );
1095 Reference
< css::uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1096 _out_rControl
= Reference
<XControl
>( xContext
->getServiceManager()->createInstanceWithContext(sControlServiceName
, xContext
), UNO_QUERY_THROW
);
1098 // tdf#150886 for calc/writer/impress make forms ignore the platform theme
1099 Reference
<XPropertySet
> xModelProperties(xControlModel
, UNO_QUERY
);
1100 Reference
<XPropertySetInfo
> xInfo
= xModelProperties
? xModelProperties
->getPropertySetInfo() : nullptr;
1101 if (xInfo
&& xInfo
->hasPropertyByName("StandardTheme"))
1102 xModelProperties
->setPropertyValue("StandardTheme", Any(!_rUnoObject
.getSdrModelFromSdrObject().AreControlsThemed()));
1104 // knit the model and the control
1105 _out_rControl
.setModel( xControlModel
);
1106 const tools::Rectangle
aRect( _rUnoObject
.GetLogicRect() );
1109 UnoControlContactHelper::adjustControlGeometry_throw(
1112 _rInitialViewTransformation
,
1113 _rInitialZoomNormalization
1116 // set design mode before peer is created,
1117 // this is also needed for accessibility
1118 _out_rControl
.setDesignMode( _rPageView
.isDesignMode() );
1120 // adjust the initial visibility according to the visibility of the layer
1121 impl_adjustControlVisibilityToLayerVisibility_throw( _out_rControl
, _rUnoObject
, _rPageView
, false, true );
1123 // add the control to the respective control container
1125 Reference
< XControlContainer
> xControlContainer( _rPageView
.getControlContainer( _rDevice
) );
1126 if ( xControlContainer
.is() )
1127 xControlContainer
->addControl( sControlServiceName
, _out_rControl
.getControl() );
1131 catch( const Exception
& )
1133 DBG_UNHANDLED_EXCEPTION("svx");
1138 // delete the control which might have been created already
1139 UnoControlContactHelper::disposeAndClearControl_nothrow( _out_rControl
);
1142 return _out_rControl
.is();
1146 bool ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow( SdrPageView
*& _out_rpPageView
)
1148 OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: already disposed!" );
1150 _out_rpPageView
= nullptr;
1151 if ( impl_isDisposed_nofail() )
1154 ObjectContactOfPageView
* pPageViewContact
= dynamic_cast< ObjectContactOfPageView
* >( &m_pAntiImpl
->GetObjectContact() );
1155 if ( pPageViewContact
)
1156 _out_rpPageView
= &pPageViewContact
->GetPageWindow().GetPageView();
1158 DBG_ASSERT( _out_rpPageView
!= nullptr, "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: this method is expected to always have success!" );
1159 return ( _out_rpPageView
!= nullptr );
1163 void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw()
1165 OSL_PRECOND( m_aControl
.is(),
1166 "ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw: only valid if we have a control!" );
1168 SdrPageView
* pPageView( nullptr );
1169 if ( !impl_getPageView_nothrow( pPageView
) )
1172 SdrUnoObj
* pUnoObject
= getUnoObject();
1176 SdrPageViewAccess
aPVAccess( *pPageView
);
1177 impl_adjustControlVisibilityToLayerVisibility_throw( m_aControl
, *pUnoObject
, aPVAccess
, m_bControlIsVisible
, false/*_bForce*/ );
1181 void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder
& _rControl
,
1182 const SdrUnoObj
& _rUnoObject
, IPageViewAccess
const & _rPageView
, bool _bIsCurrentlyVisible
, bool _bForce
)
1184 // in design mode, there is no problem with the visibility: The XControl is hidden by
1185 // default, and the Drawing Layer will simply not call our paint routine, if we're in
1186 // a hidden layer. So, only alive mode matters.
1187 if ( !_rControl
.isDesignMode() )
1189 // the layer of our object
1190 SdrLayerID nObjectLayer
= _rUnoObject
.GetLayer();
1191 // is the object we're residing in visible in this view?
1192 bool bIsObjectVisible
= _rUnoObject
.IsVisible() && _rPageView
.isLayerVisible( nObjectLayer
);
1194 if ( _bForce
|| ( bIsObjectVisible
!= _bIsCurrentlyVisible
) )
1196 _rControl
.setVisible( bIsObjectVisible
);
1202 void ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow( bool _bStart
)
1204 OSL_PRECOND( m_xContainer
.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow: no control container!" );
1205 if ( !m_xContainer
.is() )
1211 m_xContainer
->addContainerListener( this );
1213 m_xContainer
->removeContainerListener( this );
1215 catch( const Exception
& )
1217 DBG_UNHANDLED_EXCEPTION("svx");
1222 void ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow( bool _bStart
)
1224 OSL_PRECOND( m_aControl
.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow: invalid control!" );
1225 if ( !m_aControl
.is() )
1230 // listen for visibility changes
1232 m_aControl
.addWindowListener( this );
1234 m_aControl
.removeWindowListener( this );
1236 // in design mode, listen for some more aspects
1237 impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() && _bStart
);
1239 // listen for design mode changes
1240 Reference
< XModeChangeBroadcaster
> xDesignModeChanges( m_aControl
.getControl(), UNO_QUERY_THROW
);
1242 xDesignModeChanges
->addModeChangeListener( this );
1244 xDesignModeChanges
->removeModeChangeListener( this );
1246 catch( const Exception
& )
1248 DBG_UNHANDLED_EXCEPTION("svx");
1253 void ViewObjectContactOfUnoControl_Impl::impl_switchDesignModeListening_nothrow( bool _bStart
)
1255 if ( m_bIsDesignModeListening
!= _bStart
)
1257 m_bIsDesignModeListening
= _bStart
;
1258 impl_switchPropertyListening_nothrow( _bStart
);
1263 void ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow( bool _bStart
)
1265 OSL_PRECOND( m_aControl
.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow: no control!" );
1266 if ( !m_aControl
.is() )
1271 Reference
< XPropertySet
> xModelProperties( m_aControl
.getModel(), UNO_QUERY_THROW
);
1273 xModelProperties
->addPropertyChangeListener( OUString(), this );
1275 xModelProperties
->removePropertyChangeListener( OUString(), this );
1277 catch( const Exception
& )
1279 DBG_UNHANDLED_EXCEPTION("svx");
1284 bool ViewObjectContactOfUnoControl_Impl::isPrintableControl() const
1286 SdrUnoObj
* pUnoObject
= getUnoObject();
1290 bool bIsPrintable
= false;
1293 Reference
< XPropertySet
> xModelProperties( pUnoObject
->GetUnoControlModel(), UNO_QUERY_THROW
);
1294 OSL_VERIFY( xModelProperties
->getPropertyValue( "Printable" ) >>= bIsPrintable
);
1296 catch( const Exception
& )
1298 DBG_UNHANDLED_EXCEPTION("svx");
1300 return bIsPrintable
;
1304 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::disposing( const EventObject
& Source
)
1306 SolarMutexGuard aSolarGuard
;
1307 // some code below - in particular our disposal - might trigger actions which require the
1308 // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control,
1309 // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex -
1310 // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169#
1312 if ( !m_aControl
.is() )
1315 if ( ( m_aControl
== Source
.Source
)
1316 || ( m_aControl
.getModel() == Source
.Source
)
1319 // the model or the control is dying ... hmm, not much sense in that we ourself continue
1321 impl_dispose_nothrow( false );
1325 DBG_ASSERT( Source
.Source
== m_xContainer
, "ViewObjectContactOfUnoControl_Impl::disposing: Who's this?" );
1329 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::windowResized( const WindowEvent
& /*e*/ )
1331 // not interested in
1335 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::windowMoved( const WindowEvent
& /*e*/ )
1337 // not interested in
1341 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::windowShown( const EventObject
& /*e*/ )
1343 SolarMutexGuard aSolarGuard
;
1344 m_bControlIsVisible
= true;
1348 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::windowHidden( const EventObject
& /*e*/ )
1350 SolarMutexGuard aSolarGuard
;
1351 m_bControlIsVisible
= false;
1355 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::propertyChange( const PropertyChangeEvent
& /*_rEvent*/ )
1357 SolarMutexGuard aSolarGuard
;
1358 // (re)painting might require VCL operations, which need the SolarMutex
1360 OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::propertyChange: already disposed()" );
1361 if ( impl_isDisposed_nofail() )
1364 DBG_ASSERT( m_aControl
.is(), "ViewObjectContactOfUnoControl_Impl::propertyChange: " );
1365 if ( !m_aControl
.is() )
1368 // a generic property changed. If we're in design mode, we need to repaint the control
1369 if ( impl_isControlDesignMode_nothrow() )
1371 m_pAntiImpl
->propertyChange();
1376 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::modeChanged( const ModeChangeEvent
& _rSource
)
1378 SolarMutexGuard aSolarGuard
;
1380 DBG_ASSERT( _rSource
.NewMode
== "design" || _rSource
.NewMode
== "alive", "ViewObjectContactOfUnoControl_Impl::modeChanged: unexpected mode!" );
1382 m_eControlDesignMode
= _rSource
.NewMode
== "design" ? eDesign
: eAlive
;
1384 impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() );
1388 // if the control is part of an invisible layer, we need to explicitly hide it in alive mode
1389 impl_adjustControlVisibilityToLayerVisibility_throw();
1391 catch( const Exception
& )
1393 DBG_UNHANDLED_EXCEPTION("svx");
1398 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::elementInserted( const ContainerEvent
& /*_Event*/ )
1400 // not interested in
1404 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::elementRemoved( const ContainerEvent
& Event
)
1406 SolarMutexGuard aSolarGuard
;
1407 // some code below - in particular our disposal - might trigger actions which require the
1408 // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control,
1409 // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex -
1410 // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169#
1411 DBG_ASSERT( Event
.Source
== m_xContainer
, "ViewObjectContactOfUnoControl_Impl::elementRemoved: where did this come from?" );
1413 if ( m_aControl
== Event
.Element
)
1414 impl_dispose_nothrow( false );
1418 void SAL_CALL
ViewObjectContactOfUnoControl_Impl::elementReplaced( const ContainerEvent
& Event
)
1420 SolarMutexGuard aSolarGuard
;
1421 DBG_ASSERT( Event
.Source
== m_xContainer
, "ViewObjectContactOfUnoControl_Impl::elementReplaced: where did this come from?" );
1423 if ( ! ( m_aControl
== Event
.ReplacedElement
) )
1426 Reference
< XControl
> xNewControl( Event
.Element
, UNO_QUERY
);
1427 DBG_ASSERT( xNewControl
.is(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: invalid new control!" );
1428 if ( !xNewControl
.is() )
1431 ENSURE_OR_THROW( m_pOutputDeviceForWindow
, "calling this without /me having an output device should be impossible." );
1433 DBG_ASSERT( xNewControl
->getModel() == m_aControl
.getModel(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: another model at the new control?" );
1434 // another model should - in the drawing layer - also imply another SdrUnoObj, which
1435 // should also result in new ViewContact, and thus in new ViewObjectContacts
1437 impl_switchControlListening_nothrow( false );
1439 ControlHolder
aNewControl( xNewControl
);
1440 aNewControl
.setZoom( m_aControl
.getZoom() );
1441 aNewControl
.setPosSize( m_aControl
.getPosSize() );
1442 aNewControl
.setDesignMode( impl_isControlDesignMode_nothrow() );
1444 m_aControl
= xNewControl
;
1445 m_bControlIsVisible
= m_aControl
.isVisible();
1447 impl_switchControlListening_nothrow( true );
1449 m_pAntiImpl
->onControlChangedOrModified( ViewObjectContactOfUnoControl::ImplAccess() );
1453 void ViewObjectContactOfUnoControl_Impl::setControlDesignMode( bool _bDesignMode
) const
1455 if ( ( m_eControlDesignMode
!= eUnknown
) && ( _bDesignMode
== impl_isControlDesignMode_nothrow() ) )
1458 m_eControlDesignMode
= _bDesignMode
? eDesign
: eAlive
;
1460 if ( !m_aControl
.is() )
1461 // nothing to do, the setting will be respected as soon as the control
1467 m_aControl
.setDesignMode( _bDesignMode
);
1469 catch( const Exception
& )
1471 DBG_UNHANDLED_EXCEPTION("svx");
1476 //= LazyControlCreationPrimitive2D
1479 bool LazyControlCreationPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
1481 if ( !BufferedDecompositionPrimitive2D::operator==( rPrimitive
) )
1484 const LazyControlCreationPrimitive2D
* pRHS
= dynamic_cast< const LazyControlCreationPrimitive2D
* >( &rPrimitive
);
1488 if ( m_pVOCImpl
!= pRHS
->m_pVOCImpl
)
1491 if ( m_aTransformation
!= pRHS
->m_aTransformation
)
1498 void LazyControlCreationPrimitive2D::getTransformation( const ViewContactOfUnoControl
& _rVOC
, ::basegfx::B2DHomMatrix
& _out_Transformation
)
1500 // Do use model data directly to create the correct geometry. Do NOT
1501 // use getBoundRect()/getSnapRect() here; these will use the sequence of
1502 // primitives themselves in the long run.
1503 const tools::Rectangle
aSdrGeoData( _rVOC
.GetSdrUnoObj().GetGeoRect() );
1504 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(aSdrGeoData
);
1506 _out_Transformation
.identity();
1507 _out_Transformation
.set( 0, 0, aRange
.getWidth() );
1508 _out_Transformation
.set( 1, 1, aRange
.getHeight() );
1509 _out_Transformation
.set( 0, 2, aRange
.getMinX() );
1510 _out_Transformation
.set( 1, 2, aRange
.getMinY() );
1514 ::basegfx::B2DRange
LazyControlCreationPrimitive2D::getB2DRange( const ::drawinglayer::geometry::ViewInformation2D
& /*rViewInformation*/ ) const
1516 ::basegfx::B2DRange
aRange( 0.0, 0.0, 1.0, 1.0 );
1517 aRange
.transform( m_aTransformation
);
1522 void LazyControlCreationPrimitive2D::get2DDecomposition( ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
, const ::drawinglayer::geometry::ViewInformation2D
& _rViewInformation
) const
1524 #if OSL_DEBUG_LEVEL > 0
1525 ::basegfx::B2DVector aScale
, aTranslate
;
1526 double fRotate
, fShearX
;
1527 _rViewInformation
.getObjectToViewTransformation().decompose( aScale
, aTranslate
, fRotate
, fShearX
);
1529 if ( m_pVOCImpl
->hasControl() )
1530 impl_positionAndZoomControl( _rViewInformation
);
1531 BufferedDecompositionPrimitive2D::get2DDecomposition( rVisitor
, _rViewInformation
);
1535 void LazyControlCreationPrimitive2D::create2DDecomposition( ::drawinglayer::primitive2d::Primitive2DContainer
& rContainer
, const ::drawinglayer::geometry::ViewInformation2D
& _rViewInformation
) const
1537 #if OSL_DEBUG_LEVEL > 0
1538 ::basegfx::B2DVector aScale
, aTranslate
;
1539 double fRotate
, fShearX
;
1540 _rViewInformation
.getObjectToViewTransformation().decompose( aScale
, aTranslate
, fRotate
, fShearX
);
1542 const bool bHadControl
= m_pVOCImpl
->getExistentControl().is();
1544 // force control here to make it a VCL ChildWindow. Will be fetched
1545 // and used below by getExistentControl()
1546 m_pVOCImpl
->ensureControl( &_rViewInformation
.getObjectToViewTransformation() );
1547 impl_positionAndZoomControl( _rViewInformation
);
1550 const ViewContactOfUnoControl
& rViewContactOfUnoControl( m_pVOCImpl
->getViewContact() );
1551 Reference
< XControlModel
> xControlModel( rViewContactOfUnoControl
.GetSdrUnoObj().GetUnoControlModel() );
1552 const ControlHolder
& rControl( m_pVOCImpl
->getExistentControl() );
1554 if ( !bHadControl
&& rControl
.is() && rControl
.isVisible() )
1555 rControl
.invalidate();
1557 // check if we already have an XControl.
1558 if ( !xControlModel
.is() || !rControl
.is() )
1560 // use the default mechanism. This will create a ControlPrimitive2D without
1561 // handing over a XControl. If not even a XControlModel exists, it will
1562 // create the SdrObject fallback visualisation
1563 rViewContactOfUnoControl
.getViewIndependentPrimitive2DContainer(rContainer
);
1567 SdrObject
const& rSdrObj(m_pVOCImpl
->getViewContact().GetSdrObject());
1568 void const* pAnchorKey(nullptr);
1569 if (auto const pUserCall
= rSdrObj
.GetUserCall())
1571 pAnchorKey
= pUserCall
->GetPDFAnchorStructureElementKey(rSdrObj
);
1574 // create a primitive and hand over the existing xControl. This will
1575 // allow the primitive to not need to create another one on demand.
1576 rContainer
.push_back( new ::drawinglayer::primitive2d::ControlPrimitive2D(
1577 m_aTransformation
, xControlModel
, rControl
.getControl(),
1578 rSdrObj
.GetTitle(), rSdrObj
.GetDescription(), pAnchorKey
) );
1581 sal_uInt32
LazyControlCreationPrimitive2D::getPrimitive2DID() const
1583 return PRIMITIVE2D_ID_SDRCONTROLPRIMITIVE2D
;
1586 ViewObjectContactOfUnoControl::ViewObjectContactOfUnoControl( ObjectContact
& _rObjectContact
, ViewContactOfUnoControl
& _rViewContact
)
1587 :ViewObjectContactOfSdrObj( _rObjectContact
, _rViewContact
)
1588 ,m_pImpl( new ViewObjectContactOfUnoControl_Impl( this ) )
1593 ViewObjectContactOfUnoControl::~ViewObjectContactOfUnoControl()
1601 Reference
< XControl
> ViewObjectContactOfUnoControl::getControl()
1603 SolarMutexGuard aSolarGuard
;
1604 m_pImpl
->ensureControl( nullptr );
1605 return m_pImpl
->getExistentControl().getControl();
1609 Reference
< XControl
> ViewObjectContactOfUnoControl::getTemporaryControlForWindow(
1610 const vcl::Window
& _rWindow
, Reference
< XControlContainer
>& _inout_ControlContainer
, const SdrUnoObj
& _rUnoObject
)
1612 ControlHolder aControl
;
1614 InvisibleControlViewAccess
aSimulatePageView( _inout_ControlContainer
);
1615 OSL_VERIFY( ViewObjectContactOfUnoControl_Impl::createControlForDevice( aSimulatePageView
, *_rWindow
.GetOutDev(), _rUnoObject
,
1616 _rWindow
.GetOutDev()->GetViewTransformation(), _rWindow
.GetOutDev()->GetInverseViewTransformation(), aControl
) );
1617 return aControl
.getControl();
1621 void ViewObjectContactOfUnoControl::ensureControlVisibility( bool _bVisible
) const
1623 SolarMutexGuard aSolarGuard
;
1627 const ControlHolder
& rControl( m_pImpl
->getExistentControl() );
1628 if ( !rControl
.is() )
1631 // only need to care for alive mode
1632 if ( rControl
.isDesignMode() )
1635 // is the visibility correct?
1636 if ( m_pImpl
->isControlVisible() == _bVisible
)
1640 rControl
.setVisible( _bVisible
);
1641 DBG_ASSERT( m_pImpl
->isControlVisible() == _bVisible
, "ViewObjectContactOfUnoControl::ensureControlVisibility: this didn't work!" );
1642 // now this would mean that either isControlVisible is not reliable,
1643 // or that showing/hiding the window did not work as intended.
1645 catch( const Exception
& )
1647 DBG_UNHANDLED_EXCEPTION("svx");
1652 void ViewObjectContactOfUnoControl::setControlDesignMode( bool _bDesignMode
) const
1654 SolarMutexGuard aSolarGuard
;
1655 m_pImpl
->setControlDesignMode( _bDesignMode
);
1659 // when live mode is switched on, a refresh is needed. The edit mode visualisation
1660 // needs to be repainted and the now used VCL-Window needs to be positioned and
1661 // sized. Both is done from the repaint refresh.
1662 const_cast< ViewObjectContactOfUnoControl
* >(this)->ActionChanged();
1667 void ViewObjectContactOfUnoControl::createPrimitive2DSequence(const DisplayInfo
& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
) const
1669 if ( m_pImpl
->isDisposed() )
1670 // our control already died.
1671 // TODO: Is it worth re-creating the control? Finally, this is a pathological situation, it means some instance
1672 // disposed the control though it doesn't own it. So, /me thinks we should not bother here.
1675 if ( GetObjectContact().getViewInformation2D().getViewTransformation().isIdentity() )
1676 // remove this when #i115754# is fixed
1679 // ignore existing controls which are in alive mode and manually switched to "invisible" #i102090#
1680 const ControlHolder
& rControl( m_pImpl
->getExistentControl() );
1681 if ( rControl
.is() && !rControl
.isDesignMode() && !rControl
.isVisible() )
1684 rVisitor
.visit( new LazyControlCreationPrimitive2D( m_pImpl
) );
1688 bool ViewObjectContactOfUnoControl::isPrimitiveVisible( const DisplayInfo
& _rDisplayInfo
) const
1690 SolarMutexGuard aSolarGuard
;
1692 if ( m_pImpl
->hasControl() )
1694 const ::drawinglayer::geometry::ViewInformation2D
& rViewInformation( GetObjectContact().getViewInformation2D() );
1695 #if OSL_DEBUG_LEVEL > 0
1696 ::basegfx::B2DVector aScale
, aTranslate
;
1697 double fRotate
, fShearX
;
1698 rViewInformation
.getObjectToViewTransformation().decompose( aScale
, aTranslate
, fRotate
, fShearX
);
1701 if ( !rViewInformation
.getViewport().isEmpty() )
1703 // tdf#121963 check and eventually pre-multiply ViewTransformation
1704 // with GridOffset transformation to avoid alternating positions of
1705 // FormControls which are victims of the non-linear calc ViewTransformation
1706 // aka GridOffset. For other paths (e.g. repaint) this is included already
1707 // as part of the object's sequence of B2DPrimitive - representation
1708 // (see ViewObjectContact::getPrimitive2DSequence and how getGridOffset is used there)
1709 basegfx::B2DHomMatrix
aViewTransformation(rViewInformation
.getObjectToViewTransformation());
1711 if(GetObjectContact().supportsGridOffsets())
1713 const basegfx::B2DVector
& rGridOffset(getGridOffset());
1715 if(0.0 != rGridOffset
.getX() || 0.0 != rGridOffset
.getY())
1717 // pre-multiply: GridOffset needs to be applied directly to logic model data
1718 // of object coordinates, so multiply GridOffset from right to make it
1719 // work as 1st change - these objects may still be part of groups/hierarchies
1720 aViewTransformation
= aViewTransformation
* basegfx::utils::createTranslateB2DHomMatrix(rGridOffset
);
1724 m_pImpl
->positionAndZoomControl(aViewTransformation
);
1728 return ViewObjectContactOfSdrObj::isPrimitiveVisible( _rDisplayInfo
);
1732 void ViewObjectContactOfUnoControl::propertyChange()
1734 impl_onControlChangedOrModified();
1738 void ViewObjectContactOfUnoControl::ActionChanged()
1741 ViewObjectContactOfSdrObj::ActionChanged();
1742 const ControlHolder
& rControl(m_pImpl
->getExistentControl());
1744 if(!rControl
.is() || rControl
.isDesignMode())
1747 // #i93180# if layer visibility has changed and control is in live mode, it is necessary
1748 // to correct visibility to make those control vanish on SdrObject LayerID changes
1749 const SdrPageView
* pSdrPageView
= GetObjectContact().TryToGetSdrPageView();
1753 const SdrObject
& rObject
= getSdrObject();
1754 const bool bIsLayerVisible( rObject
.IsVisible() && pSdrPageView
->GetVisibleLayers().IsSet(rObject
.GetLayer()));
1756 if(rControl
.isVisible() != bIsLayerVisible
)
1758 rControl
.setVisible(bIsLayerVisible
);
1764 void ViewObjectContactOfUnoControl::impl_onControlChangedOrModified()
1766 // graphical invalidate at all views
1769 // #i93318# flush Primitive2DContainer to force recreation with updated XControlModel
1770 // since e.g. background color has changed and existing decompositions are possibly no
1771 // longer valid. Unfortunately this is not detected from ControlPrimitive2D::operator==
1772 // since it only has a uno reference to the XControlModel
1773 flushPrimitive2DSequence();
1776 UnoControlPrintOrPreviewContact::UnoControlPrintOrPreviewContact( ObjectContactOfPageView
& _rObjectContact
, ViewContactOfUnoControl
& _rViewContact
)
1777 :ViewObjectContactOfUnoControl( _rObjectContact
, _rViewContact
)
1782 UnoControlPrintOrPreviewContact::~UnoControlPrintOrPreviewContact()
1787 void UnoControlPrintOrPreviewContact::createPrimitive2DSequence(const DisplayInfo
& rDisplayInfo
, drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
) const
1789 if ( !m_pImpl
->isPrintableControl() )
1791 ViewObjectContactOfUnoControl::createPrimitive2DSequence( rDisplayInfo
, rVisitor
);
1795 } // namespace sdr::contact
1798 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */