Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / svx / source / sdr / contact / viewobjectcontactofunocontrol.cxx
blob67a30926a6309671a4d41e25b86d27bf81fdce62
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/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>
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/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>
60 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
62 #include <utility>
65 Form controls (more precise: UNO Controls) in the drawing layer are ... prone to breakage, since they have some
66 specialities which the drawing layer currently doesn't capture too well. In particular, having a living VCL
67 window as child of the document window, and coupling this Window to a drawing layer object, makes things
68 difficult sometimes.
70 Below is a list of issues which existed in the past. Whenever you change code here, you're encouraged to
71 verify those issues are still fixed. (Whenever you have some additional time, you're encouraged to write
72 an automatic test for one or more of those issues for which this is possible :)
74 https://bz.apache.org/ooo/show_bug.cgi?id=105992
75 zooming documents containing (alive) form controls improperly positions the controls
77 https://bz.apache.org/ooo/show_bug.cgi?id=104362
78 crash when copy a control
80 https://bz.apache.org/ooo/show_bug.cgi?id=104544
81 Gridcontrol duplicated after design view on/off
83 https://bz.apache.org/ooo/show_bug.cgi?id=102089
84 print preview shows control elements with property printable=false
86 https://bz.apache.org/ooo/show_bug.cgi?id=102090
87 problem with setVisible on TextControl
89 https://bz.apache.org/ooo/show_bug.cgi?id=103138
90 loop when insert a control in draw
92 https://bz.apache.org/ooo/show_bug.cgi?id=101398
93 initially-displaying a document with many controls is very slow
95 https://bz.apache.org/ooo/show_bug.cgi?id=72429
96 repaint error in form wizard in bugdoc database
98 https://bz.apache.org/ooo/show_bug.cgi?id=72694
99 form control artifacts when scrolling a text fast
104 namespace sdr::contact {
107 using namespace ::com::sun::star::awt::InvalidateStyle;
108 using ::com::sun::star::uno::Reference;
109 using ::com::sun::star::uno::XInterface;
110 using ::com::sun::star::uno::UNO_QUERY;
111 using ::com::sun::star::uno::UNO_QUERY_THROW;
112 using ::com::sun::star::uno::Exception;
113 using ::com::sun::star::awt::XControl;
114 using ::com::sun::star::awt::XControlModel;
115 using ::com::sun::star::awt::XControlContainer;
116 using ::com::sun::star::awt::XWindow2;
117 using ::com::sun::star::awt::XWindowListener;
118 using ::com::sun::star::awt::PosSize::POSSIZE;
119 using ::com::sun::star::awt::XView;
120 using ::com::sun::star::awt::WindowEvent;
121 using ::com::sun::star::beans::XPropertySet;
122 using ::com::sun::star::beans::XPropertySetInfo;
123 using ::com::sun::star::lang::XComponent;
124 using ::com::sun::star::awt::XWindowPeer;
125 using ::com::sun::star::beans::XPropertyChangeListener;
126 using ::com::sun::star::util::XModeChangeListener;
127 using ::com::sun::star::util::XModeChangeBroadcaster;
128 using ::com::sun::star::util::ModeChangeEvent;
129 using ::com::sun::star::lang::EventObject;
130 using ::com::sun::star::beans::PropertyChangeEvent;
131 using ::com::sun::star::container::XContainerListener;
132 using ::com::sun::star::container::XContainer;
133 using ::com::sun::star::container::ContainerEvent;
134 using ::com::sun::star::uno::Any;
136 namespace {
138 class ControlHolder
140 private:
141 Reference< XControl > m_xControl;
142 Reference< XWindow2 > m_xControlWindow;
143 Reference< XView > m_xControlView;
145 public:
146 ControlHolder()
150 explicit ControlHolder( const Reference< XControl >& _rxControl )
152 *this = _rxControl;
155 ControlHolder& operator=( const Reference< XControl >& _rxControl )
157 clear();
159 m_xControl = _rxControl;
160 if ( m_xControl.is() )
162 m_xControlWindow.set( m_xControl, UNO_QUERY );
163 m_xControlView.set( m_xControl, UNO_QUERY );
164 if ( !m_xControlWindow.is() || !m_xControlView.is() )
166 OSL_FAIL( "ControlHolder::operator=: invalid XControl, missing required interfaces!" );
167 clear();
171 return *this;
174 public:
175 bool is() const { return m_xControl.is() && m_xControlWindow.is() && m_xControlView.is(); }
176 void clear() { m_xControl.clear(); m_xControlWindow.clear(); m_xControlView.clear(); }
178 // delegators for the methods of the UNO interfaces
179 // Note all those will crash if called for a NULL object.
180 bool isDesignMode() const { return m_xControl->isDesignMode(); }
181 void setDesignMode( const bool _bDesign ) const { m_xControl->setDesignMode( _bDesign ); }
182 bool isVisible() const { return m_xControlWindow->isVisible(); }
183 void setVisible( const bool _bVisible ) const { m_xControlWindow->setVisible( _bVisible ); }
184 Reference< XControlModel >
185 getModel() const { return m_xControl->getModel(); }
186 void setModel( const Reference< XControlModel >& _m ) const { m_xControl->setModel( _m ); }
188 void addWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->addWindowListener( _l ); }
189 void removeWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->removeWindowListener( _l ); }
190 void setPosSize( const tools::Rectangle& _rPosSize ) const;
191 tools::Rectangle
192 getPosSize() const;
193 void setZoom( const ::basegfx::B2DVector& _rScale ) const;
194 ::basegfx::B2DVector
195 getZoom() const;
197 void invalidate() const;
199 public:
200 const Reference< XControl >& getControl() const { return m_xControl; }
203 bool operator==( const ControlHolder& _rControl, const Reference< XInterface >& _rxCompare )
205 return _rControl.getControl() == _rxCompare;
208 bool operator==( const ControlHolder& _rControl, const Any& _rxCompare )
210 return _rControl == Reference< XInterface >( _rxCompare, UNO_QUERY );
215 void ControlHolder::setPosSize( const tools::Rectangle& _rPosSize ) const
217 // no check whether we're valid, this is the responsibility of the caller
219 // don't call setPosSize when pos/size did not change #i104181#
220 ::tools::Rectangle aCurrentRect( getPosSize() );
221 if ( aCurrentRect != _rPosSize )
223 m_xControlWindow->setPosSize(
224 _rPosSize.Left(), _rPosSize.Top(), _rPosSize.GetWidth(), _rPosSize.GetHeight(),
225 POSSIZE
231 ::tools::Rectangle ControlHolder::getPosSize() const
233 // no check whether we're valid, this is the responsibility of the caller
234 return vcl::unohelper::ConvertToVCLRect( m_xControlWindow->getPosSize() );
238 void ControlHolder::setZoom( const ::basegfx::B2DVector& _rScale ) const
240 // no check whether we're valid, this is the responsibility of the caller
241 m_xControlView->setZoom( static_cast<float>(_rScale.getX()), static_cast<float>(_rScale.getY()) );
245 void ControlHolder::invalidate() const
247 Reference< XWindowPeer > xPeer( m_xControl->getPeer() );
248 if ( xPeer.is() )
250 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xPeer );
251 OSL_ENSURE( pWindow, "ControlHolder::invalidate: no implementation access!" );
252 if ( pWindow )
253 pWindow->Invalidate();
258 ::basegfx::B2DVector ControlHolder::getZoom() const
260 // no check whether we're valid, this is the responsibility of the caller
262 // Argh. Why does XView have a setZoom only, but not a getZoom?
263 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( m_xControl->getPeer() );
264 OSL_ENSURE( pWindow, "ControlHolder::getZoom: no implementation access!" );
266 ::basegfx::B2DVector aZoom( 1, 1 );
267 if ( pWindow )
269 const Fraction& rZoom( pWindow->GetZoom() );
270 aZoom.setX( static_cast<double>(rZoom) );
271 aZoom.setY( static_cast<double>(rZoom) );
273 return aZoom;
276 namespace UnoControlContactHelper {
278 /** positions a control, and sets its zoom mode, using a given transformation and output device
280 static void adjustControlGeometry_throw( const ControlHolder& _rControl, const tools::Rectangle& _rLogicBoundingRect,
281 const basegfx::B2DHomMatrix& _rViewTransformation, const ::basegfx::B2DHomMatrix& _rZoomLevelNormalization )
283 // In the LOK case, control geometry is handled by LokControlHandler
284 if (comphelper::LibreOfficeKit::isActive())
285 return;
287 OSL_PRECOND( _rControl.is(), "UnoControlContactHelper::adjustControlGeometry_throw: illegal control!" );
288 if ( !_rControl.is() )
289 return;
291 #if OSL_DEBUG_LEVEL > 0
292 ::basegfx::B2DTuple aViewScale, aViewTranslate;
293 double nViewRotate(0), nViewShearX(0);
294 _rViewTransformation.decompose( aViewScale, aViewTranslate, nViewRotate, nViewShearX );
296 ::basegfx::B2DTuple aZoomScale, aZoomTranslate;
297 double nZoomRotate(0), nZoomShearX(0);
298 _rZoomLevelNormalization.decompose( aZoomScale, aZoomTranslate, nZoomRotate, nZoomShearX );
299 #endif
301 // transform the logic bound rect, using the view transformation, to pixel coordinates
302 ::basegfx::B2DPoint aTopLeft( _rLogicBoundingRect.Left(), _rLogicBoundingRect.Top() );
303 aTopLeft *= _rViewTransformation;
304 ::basegfx::B2DPoint aBottomRight( _rLogicBoundingRect.Right(), _rLogicBoundingRect.Bottom() );
305 aBottomRight *= _rViewTransformation;
307 const tools::Rectangle aPaintRectPixel(static_cast<tools::Long>(std::round(aTopLeft.getX())),
308 static_cast<tools::Long>(std::round(aTopLeft.getY())),
309 static_cast<tools::Long>(std::round(aBottomRight.getX())),
310 static_cast<tools::Long>(std::round(aBottomRight.getY())));
311 _rControl.setPosSize( aPaintRectPixel );
313 // determine the scale from the current view transformation, and the normalization matrix
314 ::basegfx::B2DHomMatrix aObtainResolutionDependentScale( _rViewTransformation * _rZoomLevelNormalization );
315 ::basegfx::B2DVector aScale, aTranslate;
316 double fRotate, fShearX;
317 aObtainResolutionDependentScale.decompose( aScale, aTranslate, fRotate, fShearX );
318 _rControl.setZoom( aScale );
321 /** disposes the given control
323 static void disposeAndClearControl_nothrow( ControlHolder& _rControl )
327 Reference< XComponent > xControlComp = _rControl.getControl();
328 if ( xControlComp.is() )
329 xControlComp->dispose();
331 catch( const Exception& )
333 DBG_UNHANDLED_EXCEPTION("svx");
335 _rControl.clear();
340 namespace {
342 /** interface encapsulating access to an SdrPageView, stripped down to the methods we really need
344 class IPageViewAccess
346 public:
347 /** determines whether the view is currently in design mode
349 virtual bool isDesignMode() const = 0;
351 /** retrieves the control container for a given output device
353 virtual Reference< XControlContainer >
354 getControlContainer( const OutputDevice& _rDevice ) const = 0;
356 /** determines whether a given layer is visible
358 virtual bool isLayerVisible( SdrLayerID _nLayerID ) const = 0;
360 protected:
361 ~IPageViewAccess() {}
364 /** is a ->IPageViewAccess implementation based on a real ->SdrPageView instance
366 class SdrPageViewAccess : public IPageViewAccess
368 const SdrPageView& m_rPageView;
369 public:
370 explicit SdrPageViewAccess( const SdrPageView& _rPageView ) : m_rPageView( _rPageView ) { }
372 virtual ~SdrPageViewAccess() {}
374 virtual bool isDesignMode() const override;
375 virtual Reference< XControlContainer >
376 getControlContainer( const OutputDevice& _rDevice ) const override;
377 virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override;
382 bool SdrPageViewAccess::isDesignMode() const
384 return m_rPageView.GetView().IsDesignMode();
388 Reference< XControlContainer > SdrPageViewAccess::getControlContainer( const OutputDevice& _rDevice ) const
390 Reference< XControlContainer > xControlContainer = m_rPageView.GetControlContainer( _rDevice );
391 DBG_ASSERT( xControlContainer.is() || nullptr == m_rPageView.FindPageWindow( _rDevice ),
392 "SdrPageViewAccess::getControlContainer: the output device is known, but there is no control container for it?" );
393 return xControlContainer;
397 bool SdrPageViewAccess::isLayerVisible( SdrLayerID _nLayerID ) const
399 return m_rPageView.GetVisibleLayers().IsSet( _nLayerID );
402 namespace {
404 /** is a ->IPageViewAccess implementation which can be used to create an invisible control for
405 an arbitrary window
407 class InvisibleControlViewAccess : public IPageViewAccess
409 private:
410 Reference< XControlContainer >& m_rControlContainer;
411 public:
412 explicit InvisibleControlViewAccess( Reference< XControlContainer >& _inout_ControlContainer )
413 :m_rControlContainer( _inout_ControlContainer )
417 virtual ~InvisibleControlViewAccess() {}
419 virtual bool isDesignMode() const override;
420 virtual Reference< XControlContainer >
421 getControlContainer( const OutputDevice& _rDevice ) const override;
422 virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override;
427 bool InvisibleControlViewAccess::isDesignMode() const
429 return true;
433 Reference< XControlContainer > InvisibleControlViewAccess::getControlContainer( const OutputDevice& _rDevice ) const
435 if ( !m_rControlContainer.is() )
437 const vcl::Window* pWindow = _rDevice.GetOwnerWindow();
438 OSL_ENSURE( pWindow, "InvisibleControlViewAccess::getControlContainer: expected to be called for a window only!" );
439 if ( pWindow )
440 m_rControlContainer = VCLUnoHelper::CreateControlContainer( const_cast< vcl::Window* >( pWindow ) );
442 return m_rControlContainer;
446 bool InvisibleControlViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const
448 return false;
451 namespace {
453 //= DummyPageViewAccess
455 /** is a ->IPageViewAccess implementation which can be used to create a control for an arbitrary
456 non-Window device
458 The implementation will report the "PageView" as being in design mode, all layers to be visible,
459 and will not return any ControlContainer, so all control container related features (notifications etc)
460 are not available.
462 class DummyPageViewAccess : public IPageViewAccess
464 public:
465 DummyPageViewAccess()
469 virtual ~DummyPageViewAccess() {}
471 virtual bool isDesignMode() const override;
472 virtual Reference< XControlContainer >
473 getControlContainer( const OutputDevice& _rDevice ) const override;
474 virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override;
479 bool DummyPageViewAccess::isDesignMode() const
481 return true;
485 Reference< XControlContainer > DummyPageViewAccess::getControlContainer( const OutputDevice& /*_rDevice*/ ) const
487 return nullptr;
491 bool DummyPageViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const
493 return true;
497 //= ViewObjectContactOfUnoControl_Impl
499 typedef ::cppu::WeakImplHelper < XWindowListener
500 , XPropertyChangeListener
501 , XContainerListener
502 , XModeChangeListener
503 > ViewObjectContactOfUnoControl_Impl_Base;
505 class ViewObjectContactOfUnoControl_Impl:
506 public ViewObjectContactOfUnoControl_Impl_Base
508 private:
509 // tdf#41935 note that access to members is protected with SolarMutex;
510 // the class previously had its own mutex but that is prone to deadlock
512 /// the instance whose IMPL we are
513 ViewObjectContactOfUnoControl* m_pAntiImpl;
515 /// are we currently inside impl_ensureControl_nothrow?
516 bool m_bCreatingControl;
518 /// the control we're responsible for
519 ControlHolder m_aControl;
521 /// the ControlContainer where we inserted our control
522 Reference< XContainer > m_xContainer;
524 /// the output device for which the control was created
525 VclPtr<OutputDevice> m_pOutputDeviceForWindow;
527 /// flag indicating whether the control is currently visible
528 bool m_bControlIsVisible;
530 /// are we currently listening at a design mode control?
531 bool m_bIsDesignModeListening;
533 enum ViewControlMode
535 eDesign,
536 eAlive,
537 eUnknown
539 /// is the control currently in design mode?
540 mutable ViewControlMode m_eControlDesignMode;
542 ::basegfx::B2DHomMatrix m_aZoomLevelNormalization;
544 public:
545 explicit ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl );
546 ViewObjectContactOfUnoControl_Impl(const ViewObjectContactOfUnoControl_Impl&) = delete;
547 ViewObjectContactOfUnoControl_Impl& operator=(const ViewObjectContactOfUnoControl_Impl&) = delete;
549 /** disposes the instance, which is nonfunctional afterwards
551 void dispose();
553 /** determines whether the instance is disposed
555 bool isDisposed() const { return impl_isDisposed_nofail(); }
557 /** returns the SdrUnoObject associated with the ViewContact
559 @precond
560 We're not disposed.
562 SdrUnoObj* getUnoObject() const;
564 /** ensures that we have an ->XControl
566 Must only be called if a control is needed when no DisplayInfo is present, yet.
568 For creating a control, an ->OutputDevice is needed, and an ->SdrPageView. Both will be obtained
569 from a ->ObjectContactOfPageView. So, if our (anti-impl's) object contact is not a ->ObjectContactOfPageView,
570 this method fill fail.
572 Failure of this method will be reported via an assertion in a non-product version.
574 void ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL );
576 /** returns our XControl, if it already has been created
578 If you want to ensure that the control exists before accessing it, use ->ensureControl
580 const ControlHolder&
581 getExistentControl() const { return m_aControl; }
583 bool
584 hasControl() const { return m_aControl.is(); }
586 /** positions our XControl according to the geometry settings in the SdrUnoObj, modified by the given
587 transformation, and sets proper zoom settings according to our device
589 @precond
590 ->m_pOutputDeviceForWindow and ->m_aControl are not <NULL/>
592 void positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const;
594 /** determines whether or not our control is printable
596 Effectively, this method returns the value of the "Printable" property
597 of the control's model. If we have no control, <FALSE/> is returned.
599 bool isPrintableControl() const;
601 /** sets the design mode on the control, or at least remembers the flag for the
602 time the control is created
604 void setControlDesignMode( bool _bDesignMode ) const;
606 /** determines whether our control is currently visible
607 @nofail
609 bool isControlVisible() const { return m_bControlIsVisible; }
611 /// creates an XControl for the given device and SdrUnoObj
612 static bool
613 createControlForDevice(
614 IPageViewAccess const & _rPageView,
615 const OutputDevice& _rDevice,
616 const SdrUnoObj& _rUnoObject,
617 const basegfx::B2DHomMatrix& _rInitialViewTransformation,
618 const basegfx::B2DHomMatrix& _rInitialZoomNormalization,
619 ControlHolder& _out_rControl
622 const ViewContactOfUnoControl&
623 getViewContact() const
625 ENSURE_OR_THROW( !impl_isDisposed_nofail(), "already disposed" );
626 return static_cast< const ViewContactOfUnoControl& >( m_pAntiImpl->GetViewContact() );
629 protected:
630 virtual ~ViewObjectContactOfUnoControl_Impl() override;
632 // XEventListener
633 virtual void SAL_CALL disposing( const EventObject& Source ) override;
635 // XWindowListener
636 virtual void SAL_CALL windowResized( const WindowEvent& e ) override;
637 virtual void SAL_CALL windowMoved( const WindowEvent& e ) override;
638 virtual void SAL_CALL windowShown( const EventObject& e ) override;
639 virtual void SAL_CALL windowHidden( const EventObject& e ) override;
641 // XPropertyChangeListener
642 virtual void SAL_CALL propertyChange( const PropertyChangeEvent& evt ) override;
644 // XModeChangeListener
645 virtual void SAL_CALL modeChanged( const ModeChangeEvent& _rSource ) override;
647 // XContainerListener
648 virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override;
649 virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override;
650 virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override;
652 private:
653 /** retrieves the SdrPageView which our associated SdrPageViewWindow belongs to
655 @param out_rpPageView
656 a reference to a pointer holding, upon return, the desired SdrPageView
658 @return
659 <TRUE/> if and only if a ->SdrPageView could be obtained
661 @precond
662 We really belong to an SdrPageViewWindow. Perhaps (I'm not sure ATM :)
663 there are instance for which this might not be true, but those instances
664 should never have a need to call this method.
666 @precond
667 We're not disposed.
669 @postcond
670 The method expects success, if it returns with <FALSE/>, this will have been
671 asserted.
673 @nothrow
675 bool impl_getPageView_nothrow( SdrPageView*& _out_rpPageView );
677 /** adjusts the control visibility so it respects its layer's visibility
679 @precond
680 ->m_aControl is not <NULL/>
682 @precond
683 We're not disposed.
685 @precond
686 We really belong to an SdrPageViewWindow. There are instance for which this
687 might not be true, but those instances should never have a need to call
688 this method.
690 void impl_adjustControlVisibilityToLayerVisibility_throw();
692 /** adjusts the control visibility so it respects its layer's visibility
694 The control must never be visible if it's in design mode.
695 In alive mode, it must be visibility if and only it's on a visible layer.
697 @param _rxControl
698 the control whose visibility is to be adjusted
700 @param _rPageView
701 provides access to the attributes of the SdrPageView which the control finally belongs to
703 @param _rUnoObject
704 our SdrUnoObj
706 @param _bIsCurrentlyVisible
707 determines whether the control is currently visible. Note that this is only a shortcut for
708 querying _rxControl for the XWindow2 interface, and calling isVisible at this interface.
709 This shortcut has been chosen since the caller usually already has this information.
710 If _bForce is <TRUE/>, _bIsCurrentlyVisible is ignored.
712 @param _bForce
713 set to <TRUE/> if you want to force a ->XWindow::setVisible call,
714 no matter if the control visibility is already correct
716 @precond
717 We're not disposed.
719 static void impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rxControl, const SdrUnoObj& _rUnoObject,
720 IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce );
722 /** starts or stops listening at various aspects of our control
724 @precond
725 ->m_aControl is not <NULL/>
727 void impl_switchControlListening_nothrow( bool _bStart );
729 /** starts or stops listening at our control container
731 @precond
732 ->m_xContainer is not <NULL/>
734 void impl_switchContainerListening_nothrow( bool _bStart );
736 /** starts or stops listening at the control for design-mode relevant facets
738 void impl_switchDesignModeListening_nothrow( bool _bStart );
740 /** starts or stops listening for all properties at our control
742 @param _bStart
743 determines whether to start or to stop listening
745 @precond
746 ->m_aControl is not <NULL/>
748 void impl_switchPropertyListening_nothrow( bool _bStart );
750 /** disposes the instance
751 @param _bAlsoDisposeControl
752 determines whether the XControl should be disposed, too
754 void impl_dispose_nothrow( bool _bAlsoDisposeControl );
756 /** determines whether the instance is disposed
757 @nofail
759 bool impl_isDisposed_nofail() const { return m_pAntiImpl == nullptr; }
761 /** determines whether the control currently is in design mode
763 @precond
764 The design mode must already be known. It is known when we first had access to
765 an SdrPageView (which carries this flag), or somebody explicitly set it from
766 outside.
768 bool impl_isControlDesignMode_nothrow() const
770 DBG_ASSERT( m_eControlDesignMode != eUnknown, "ViewObjectContactOfUnoControl_Impl::impl_isControlDesignMode_nothrow: mode is still unknown!" );
771 return m_eControlDesignMode == eDesign;
774 /** ensures that we have a control for the given PageView/OutputDevice
776 bool impl_ensureControl_nothrow(
777 IPageViewAccess const & _rPageView,
778 const OutputDevice& _rDevice,
779 const basegfx::B2DHomMatrix& _rInitialViewTransformation
782 const OutputDevice& impl_getOutputDevice_throw() const;
785 namespace {
787 class LazyControlCreationPrimitive2D : public ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D
789 private:
790 typedef ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D BufferedDecompositionPrimitive2D;
792 protected:
793 virtual void
794 get2DDecomposition(
795 ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor,
796 const ::drawinglayer::geometry::ViewInformation2D& rViewInformation
797 ) const override;
799 virtual ::drawinglayer::primitive2d::Primitive2DReference create2DDecomposition(
800 const ::drawinglayer::geometry::ViewInformation2D& rViewInformation
801 ) const override;
803 virtual ::basegfx::B2DRange
804 getB2DRange(
805 const ::drawinglayer::geometry::ViewInformation2D& rViewInformation
806 ) const override;
808 public:
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 );
823 private:
824 void impl_positionAndZoomControl( const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const
826 if ( !_rViewInformation.getViewport().isEmpty() )
827 m_pVOCImpl->positionAndZoomControl( _rViewInformation.getObjectToViewTransformation() );
830 private:
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 );
858 #endif
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 );
868 #endif
872 ViewObjectContactOfUnoControl_Impl::~ViewObjectContactOfUnoControl_Impl()
874 if ( !impl_isDisposed_nofail() )
876 acquire();
877 dispose();
883 void ViewObjectContactOfUnoControl_Impl::impl_dispose_nothrow( bool _bAlsoDisposeControl )
885 if ( impl_isDisposed_nofail() )
886 return;
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 );
898 m_aControl.clear();
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() )
918 return nullptr;
919 auto pRet = dynamic_cast< SdrUnoObj* >( m_pAntiImpl->GetViewContact().TryToGetSdrObject() );
920 DBG_ASSERT( pRet || !m_pAntiImpl->GetViewContact().TryToGetSdrObject(),
921 "ViewObjectContactOfUnoControl_Impl::getUnoObject: invalid SdrObject!" );
922 return pRet;
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() )
930 return;
934 SdrUnoObj* pUnoObject = getUnoObject();
935 if ( pUnoObject )
937 const tools::Rectangle aRect( pUnoObject->GetLogicRect() );
938 UnoControlContactHelper::adjustControlGeometry_throw( m_aControl, aRect, _rViewTransformation, m_aZoomLevelNormalization );
940 else
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() )
954 return;
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(
962 aPVAccess,
963 rDevice,
964 _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation()
966 return;
969 DummyPageViewAccess aNoPageView;
970 const OutputDevice& rDevice( impl_getOutputDevice_throw() );
971 impl_ensureControl_nothrow(
972 aNoPageView,
973 rDevice,
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();
984 if( oPageOutputDev )
985 return *oPageOutputDev;
987 const OutputDevice* pDevice = m_pAntiImpl->GetObjectContact().TryToGetOutputDevice();
988 ENSURE_OR_THROW( pDevice, "no output device -> no control" );
989 return *pDevice;
993 namespace
995 void lcl_resetFlag( bool& rbFlag )
997 rbFlag = false;
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#
1017 return false;
1020 m_bCreatingControl = true;
1021 ::comphelper::ScopeGuard aGuard([&] () { lcl_resetFlag(m_bCreatingControl); });
1023 if ( m_aControl.is() )
1025 if ( m_pOutputDeviceForWindow.get() == &_rDevice )
1026 return true;
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,
1033 // is it? #i74523#
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();
1041 if ( !pUnoObject )
1042 return false;
1044 ControlHolder aControl;
1045 if ( !createControlForDevice( _rPageView, _rDevice, *pUnoObject, _rInitialViewTransformation, m_aZoomLevelNormalization, aControl ) )
1046 return false;
1048 m_pOutputDeviceForWindow = const_cast< OutputDevice * >( &_rDevice );
1049 m_aControl = std::move(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() )
1088 return false;
1090 bool bSuccess = false;
1093 const OUString& sControlServiceName( _rUnoObject.GetUnoControlTypeName() );
1095 const 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(u"StandardTheme"_ustr))
1102 xModelProperties->setPropertyValue(u"StandardTheme"_ustr, Any(!_rUnoObject.getSdrModelFromSdrObject().AreControlsThemed()));
1104 // knit the model and the control
1105 _out_rControl.setModel( xControlModel );
1106 const tools::Rectangle aRect( _rUnoObject.GetLogicRect() );
1108 // proper geometry
1109 UnoControlContactHelper::adjustControlGeometry_throw(
1110 _out_rControl,
1111 aRect,
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
1124 // do this last
1125 Reference< XControlContainer > xControlContainer( _rPageView.getControlContainer( _rDevice ) );
1126 if ( xControlContainer.is() )
1127 xControlContainer->addControl( sControlServiceName, _out_rControl.getControl() );
1129 bSuccess = true;
1131 catch( const Exception& )
1133 DBG_UNHANDLED_EXCEPTION("svx");
1136 if ( !bSuccess )
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() )
1152 return false;
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 ) )
1170 return;
1172 SdrUnoObj* pUnoObject = getUnoObject();
1173 if ( !pUnoObject )
1174 return;
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() )
1206 return;
1210 if ( _bStart )
1211 m_xContainer->addContainerListener( this );
1212 else
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() )
1226 return;
1230 // listen for visibility changes
1231 if ( _bStart )
1232 m_aControl.addWindowListener( this );
1233 else
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 );
1241 if ( _bStart )
1242 xDesignModeChanges->addModeChangeListener( this );
1243 else
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() )
1267 return;
1271 Reference< XPropertySet > xModelProperties( m_aControl.getModel(), UNO_QUERY_THROW );
1272 if ( _bStart )
1273 xModelProperties->addPropertyChangeListener( OUString(), this );
1274 else
1275 xModelProperties->removePropertyChangeListener( OUString(), this );
1277 catch( const Exception& )
1279 DBG_UNHANDLED_EXCEPTION("svx");
1284 bool ViewObjectContactOfUnoControl_Impl::isPrintableControl() const
1286 SdrUnoObj* pUnoObject = getUnoObject();
1287 if ( !pUnoObject )
1288 return false;
1290 bool bIsPrintable = false;
1293 Reference< XPropertySet > xModelProperties( pUnoObject->GetUnoControlModel(), UNO_QUERY_THROW );
1294 OSL_VERIFY( xModelProperties->getPropertyValue( u"Printable"_ustr ) >>= 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() )
1313 return;
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
1320 // living
1321 impl_dispose_nothrow( false );
1322 return;
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() )
1362 return;
1364 DBG_ASSERT( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::propertyChange: " );
1365 if ( !m_aControl.is() )
1366 return;
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 ) )
1424 return;
1426 Reference< XControl > xNewControl( Event.Element, UNO_QUERY );
1427 DBG_ASSERT( xNewControl.is(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: invalid new control!" );
1428 if ( !xNewControl.is() )
1429 return;
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() ) )
1456 // nothing to do
1457 return;
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
1462 // is created
1463 return;
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 ) )
1482 return false;
1484 const LazyControlCreationPrimitive2D* pRHS = dynamic_cast< const LazyControlCreationPrimitive2D* >( &rPrimitive );
1485 if ( !pRHS )
1486 return false;
1488 if ( m_pVOCImpl != pRHS->m_pVOCImpl )
1489 return false;
1491 if ( m_aTransformation != pRHS->m_aTransformation )
1492 return false;
1494 return true;
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 );
1518 return aRange;
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 );
1528 #endif
1529 if ( m_pVOCImpl->hasControl() )
1530 impl_positionAndZoomControl( _rViewInformation );
1531 BufferedDecompositionPrimitive2D::get2DDecomposition( rVisitor, _rViewInformation );
1535 ::drawinglayer::primitive2d::Primitive2DReference LazyControlCreationPrimitive2D::create2DDecomposition( 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 );
1541 #endif
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 );
1549 // get needed data
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 ::drawinglayer::primitive2d::Primitive2DContainer aContainer;
1564 rViewContactOfUnoControl.getViewIndependentPrimitive2DContainer(aContainer);
1565 return new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer));
1568 SdrObject const& rSdrObj(m_pVOCImpl->getViewContact().GetSdrObject());
1569 void const* pAnchorKey(nullptr);
1570 if (auto const pUserCall = rSdrObj.GetUserCall())
1572 pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(rSdrObj);
1575 // create a primitive and hand over the existing xControl. This will
1576 // allow the primitive to not need to create another one on demand.
1577 return new ::drawinglayer::primitive2d::ControlPrimitive2D(
1578 m_aTransformation, xControlModel, rControl.getControl(),
1579 rSdrObj.GetTitle(), rSdrObj.GetDescription(), pAnchorKey);
1582 sal_uInt32 LazyControlCreationPrimitive2D::getPrimitive2DID() const
1584 return PRIMITIVE2D_ID_SDRCONTROLPRIMITIVE2D;
1587 ViewObjectContactOfUnoControl::ViewObjectContactOfUnoControl( ObjectContact& _rObjectContact, ViewContactOfUnoControl& _rViewContact )
1588 :ViewObjectContactOfSdrObj( _rObjectContact, _rViewContact )
1589 ,m_pImpl( new ViewObjectContactOfUnoControl_Impl( this ) )
1594 ViewObjectContactOfUnoControl::~ViewObjectContactOfUnoControl()
1596 m_pImpl->dispose();
1597 m_pImpl = nullptr;
1602 Reference< XControl > ViewObjectContactOfUnoControl::getControl()
1604 SolarMutexGuard aSolarGuard;
1605 m_pImpl->ensureControl( nullptr );
1606 return m_pImpl->getExistentControl().getControl();
1610 Reference< XControl > ViewObjectContactOfUnoControl::getTemporaryControlForWindow(
1611 const vcl::Window& _rWindow, Reference< XControlContainer >& _inout_ControlContainer, const SdrUnoObj& _rUnoObject )
1613 ControlHolder aControl;
1615 InvisibleControlViewAccess aSimulatePageView( _inout_ControlContainer );
1616 OSL_VERIFY( ViewObjectContactOfUnoControl_Impl::createControlForDevice( aSimulatePageView, *_rWindow.GetOutDev(), _rUnoObject,
1617 _rWindow.GetOutDev()->GetViewTransformation(), _rWindow.GetOutDev()->GetInverseViewTransformation(), aControl ) );
1618 return aControl.getControl();
1622 void ViewObjectContactOfUnoControl::ensureControlVisibility( bool _bVisible ) const
1624 SolarMutexGuard aSolarGuard;
1628 const ControlHolder& rControl( m_pImpl->getExistentControl() );
1629 if ( !rControl.is() )
1630 return;
1632 // only need to care for alive mode
1633 if ( rControl.isDesignMode() )
1634 return;
1636 // is the visibility correct?
1637 if ( m_pImpl->isControlVisible() == _bVisible )
1638 return;
1640 // no -> adjust it
1641 rControl.setVisible( _bVisible );
1642 DBG_ASSERT( m_pImpl->isControlVisible() == _bVisible, "ViewObjectContactOfUnoControl::ensureControlVisibility: this didn't work!" );
1643 // now this would mean that either isControlVisible is not reliable,
1644 // or that showing/hiding the window did not work as intended.
1646 catch( const Exception& )
1648 DBG_UNHANDLED_EXCEPTION("svx");
1653 void ViewObjectContactOfUnoControl::setControlDesignMode( bool _bDesignMode ) const
1655 SolarMutexGuard aSolarGuard;
1656 m_pImpl->setControlDesignMode( _bDesignMode );
1658 if(!_bDesignMode)
1660 // when live mode is switched on, a refresh is needed. The edit mode visualisation
1661 // needs to be repainted and the now used VCL-Window needs to be positioned and
1662 // sized. Both is done from the repaint refresh.
1663 const_cast< ViewObjectContactOfUnoControl* >(this)->ActionChanged();
1668 void ViewObjectContactOfUnoControl::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
1670 if ( m_pImpl->isDisposed() )
1671 // our control already died.
1672 // TODO: Is it worth re-creating the control? Finally, this is a pathological situation, it means some instance
1673 // disposed the control though it doesn't own it. So, /me thinks we should not bother here.
1674 return;
1676 if ( GetObjectContact().getViewInformation2D().getViewTransformation().isIdentity() )
1677 // remove this when #i115754# is fixed
1678 return;
1680 // ignore existing controls which are in alive mode and manually switched to "invisible" #i102090#
1681 const ControlHolder& rControl( m_pImpl->getExistentControl() );
1682 if ( rControl.is() && !rControl.isDesignMode() && !rControl.isVisible() )
1683 return;
1685 rVisitor.visit( new LazyControlCreationPrimitive2D( m_pImpl ) );
1689 bool ViewObjectContactOfUnoControl::isPrimitiveVisible( const DisplayInfo& _rDisplayInfo ) const
1691 SolarMutexGuard aSolarGuard;
1693 if ( m_pImpl->hasControl() )
1695 const ::drawinglayer::geometry::ViewInformation2D& rViewInformation( GetObjectContact().getViewInformation2D() );
1696 #if OSL_DEBUG_LEVEL > 0
1697 ::basegfx::B2DVector aScale, aTranslate;
1698 double fRotate, fShearX;
1699 rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX );
1700 #endif
1702 if ( !rViewInformation.getViewport().isEmpty() )
1704 // tdf#121963 check and eventually pre-multiply ViewTransformation
1705 // with GridOffset transformation to avoid alternating positions of
1706 // FormControls which are victims of the non-linear calc ViewTransformation
1707 // aka GridOffset. For other paths (e.g. repaint) this is included already
1708 // as part of the object's sequence of B2DPrimitive - representation
1709 // (see ViewObjectContact::getPrimitive2DSequence and how getGridOffset is used there)
1710 basegfx::B2DHomMatrix aViewTransformation(rViewInformation.getObjectToViewTransformation());
1712 if(GetObjectContact().supportsGridOffsets())
1714 const basegfx::B2DVector& rGridOffset(getGridOffset());
1716 if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
1718 // pre-multiply: GridOffset needs to be applied directly to logic model data
1719 // of object coordinates, so multiply GridOffset from right to make it
1720 // work as 1st change - these objects may still be part of groups/hierarchies
1721 aViewTransformation = aViewTransformation * basegfx::utils::createTranslateB2DHomMatrix(rGridOffset);
1725 m_pImpl->positionAndZoomControl(aViewTransformation);
1729 return ViewObjectContactOfSdrObj::isPrimitiveVisible( _rDisplayInfo );
1733 void ViewObjectContactOfUnoControl::propertyChange()
1735 impl_onControlChangedOrModified();
1739 void ViewObjectContactOfUnoControl::ActionChanged()
1741 // call parent
1742 ViewObjectContactOfSdrObj::ActionChanged();
1743 const ControlHolder& rControl(m_pImpl->getExistentControl());
1745 if(!rControl.is() || rControl.isDesignMode())
1746 return;
1748 // #i93180# if layer visibility has changed and control is in live mode, it is necessary
1749 // to correct visibility to make those control vanish on SdrObject LayerID changes
1750 const SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
1752 if(pSdrPageView)
1754 const SdrObject& rObject = getSdrObject();
1755 const bool bIsLayerVisible( rObject.IsVisible() && pSdrPageView->GetVisibleLayers().IsSet(rObject.GetLayer()));
1757 if(rControl.isVisible() != bIsLayerVisible)
1759 rControl.setVisible(bIsLayerVisible);
1765 void ViewObjectContactOfUnoControl::impl_onControlChangedOrModified()
1767 // graphical invalidate at all views
1768 ActionChanged();
1770 // #i93318# flush Primitive2DContainer to force recreation with updated XControlModel
1771 // since e.g. background color has changed and existing decompositions are possibly no
1772 // longer valid. Unfortunately this is not detected from ControlPrimitive2D::operator==
1773 // since it only has a uno reference to the XControlModel
1774 flushPrimitive2DSequence();
1777 UnoControlPrintOrPreviewContact::UnoControlPrintOrPreviewContact( ObjectContactOfPageView& _rObjectContact, ViewContactOfUnoControl& _rViewContact )
1778 :ViewObjectContactOfUnoControl( _rObjectContact, _rViewContact )
1783 UnoControlPrintOrPreviewContact::~UnoControlPrintOrPreviewContact()
1788 void UnoControlPrintOrPreviewContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor ) const
1790 if ( !m_pImpl->isPrintableControl() )
1791 return;
1792 ViewObjectContactOfUnoControl::createPrimitive2DSequence( rDisplayInfo, rVisitor );
1796 } // namespace sdr::contact
1799 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */