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 .
20 #include "propcontroller.hxx"
21 #include "handlerhelper.hxx"
22 #include "standardcontrol.hxx"
23 #include "linedescriptor.hxx"
24 #include <strings.hrc>
25 #include "propertyeditor.hxx"
26 #include "modulepcr.hxx"
27 #include "formstrings.hxx"
28 #include "formbrowsertools.hxx"
29 #include "propertycomposer.hxx"
31 #include <com/sun/star/awt/XWindow.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/lang/NoSupportException.hpp>
34 #include <com/sun/star/inspection/PropertyControlType.hpp>
35 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
36 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
37 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
38 #include <com/sun/star/util/VetoException.hpp>
39 #include <tools/debug.hxx>
40 #include <comphelper/diagnose_ex.hxx>
41 #include <toolkit/helper/vclunohelper.hxx>
42 #include <vcl/svapp.hxx>
43 #include <vcl/weld.hxx>
44 #include <vcl/weldutils.hxx>
45 #include <osl/mutex.hxx>
46 #include <cppuhelper/queryinterface.hxx>
47 #include <cppuhelper/supportsservice.hxx>
50 #include <sal/log.hxx>
54 using namespace ::com::sun::star
;
55 using namespace ::com::sun::star::uno
;
56 using namespace ::com::sun::star::awt
;
57 using namespace ::com::sun::star::beans
;
58 using namespace ::com::sun::star::script
;
59 using namespace ::com::sun::star::lang
;
60 using namespace ::com::sun::star::container
;
61 using namespace ::com::sun::star::frame
;
62 using namespace ::com::sun::star::util
;
63 using namespace ::com::sun::star::inspection
;
64 using namespace ::com::sun::star::ucb
;
65 using namespace ::comphelper
;
67 //= OPropertyBrowserController
68 OPropertyBrowserController::OPropertyBrowserController( const Reference
< XComponentContext
>& _rxContext
)
69 :m_xContext(_rxContext
)
70 ,m_aDisposeListeners( m_aMutex
)
71 ,m_aControlObservers( m_aMutex
)
72 ,m_bContainerFocusListening( false )
73 ,m_bSuspendingPropertyHandlers( false )
74 ,m_bConstructed( false )
75 ,m_bBindingIntrospectee( false )
79 OPropertyBrowserController::~OPropertyBrowserController()
81 // stop listening for property changes
83 stopInspection( true );
86 IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController
, OPropertyBrowserController_Base
)
88 Any SAL_CALL
OPropertyBrowserController::queryInterface( const Type
& _rType
)
90 Any aReturn
= OPropertyBrowserController_Base::queryInterface( _rType
);
91 if ( !aReturn
.hasValue() )
92 aReturn
= ::cppu::queryInterface(
94 static_cast< XObjectInspectorUI
* >( this )
100 void OPropertyBrowserController::startContainerWindowListening()
102 if (m_bContainerFocusListening
)
107 Reference
< XWindow
> xContainerWindow
= m_xFrame
->getContainerWindow();
108 if (xContainerWindow
.is())
110 xContainerWindow
->addFocusListener(this);
111 m_bContainerFocusListening
= true;
115 DBG_ASSERT(m_bContainerFocusListening
, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!");
119 void OPropertyBrowserController::stopContainerWindowListening()
121 if (!m_bContainerFocusListening
)
126 Reference
< XWindow
> xContainerWindow
= m_xFrame
->getContainerWindow();
127 if (xContainerWindow
.is())
129 xContainerWindow
->removeFocusListener(this);
130 m_bContainerFocusListening
= false;
134 DBG_ASSERT(!m_bContainerFocusListening
, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!");
138 Reference
< XObjectInspectorModel
> SAL_CALL
OPropertyBrowserController::getInspectorModel()
144 void OPropertyBrowserController::impl_initializeView_nothrow()
146 OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
150 if ( !m_xModel
.is() )
156 getPropertyBox().EnableHelpSection( m_xModel
->getHasHelpSection() );
158 catch( const Exception
& )
160 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
165 bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const
167 if ( !m_xModel
.is() )
170 return m_xModel
->getIsReadOnly();
174 void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen
) const
178 Reference
< XPropertySet
> xModelProperties( m_xModel
, UNO_QUERY
);
179 if ( !xModelProperties
.is() )
180 // okay, so the model doesn't want to change its properties
181 // dynamically - fine with us
184 void (SAL_CALL
XPropertySet::*pListenerOperation
)( const OUString
&, const Reference
< XPropertyChangeListener
>& )
185 = _bDoListen
? &XPropertySet::addPropertyChangeListener
: &XPropertySet::removePropertyChangeListener
;
187 (xModelProperties
.get()->*pListenerOperation
)(
188 OUString( "IsReadOnly" ),
189 const_cast< OPropertyBrowserController
* >( this )
192 catch( const Exception
& )
194 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
199 void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference
< XObjectInspectorModel
>& _rxInspectorModel
)
201 impl_startOrStopModelListening_nothrow( false );
202 m_xModel
= _rxInspectorModel
;
203 impl_startOrStopModelListening_nothrow( true );
205 // initialize the view, if we already have one
207 impl_initializeView_nothrow();
209 // inspect again, if we already have inspectees
210 if ( !m_aInspectedObjects
.empty() )
211 impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects
) );
215 void SAL_CALL
OPropertyBrowserController::setInspectorModel( const Reference
< XObjectInspectorModel
>& _inspectorModel
)
217 ::osl::MutexGuard
aGuard( m_aMutex
);
219 if ( m_xModel
== _inspectorModel
)
222 impl_bindToNewModel_nothrow( _inspectorModel
);
226 Reference
< XObjectInspectorUI
> SAL_CALL
OPropertyBrowserController::getInspectorUI()
228 // we're derived from this interface, though we do not expose it in queryInterface and getTypes.
233 void SAL_CALL
OPropertyBrowserController::inspect( const Sequence
< Reference
< XInterface
> >& _rObjects
)
235 SolarMutexGuard aSolarGuard
;
236 ::osl::MutexGuard
aGuard( m_aMutex
);
238 if ( m_bSuspendingPropertyHandlers
|| !suspendAll_nothrow() )
239 { // we already are trying to suspend the component (this is somewhere up the stack)
240 // OR one of our property handlers raised a veto against closing. Well, we *need* to close
241 // it in order to inspect another object.
242 throw VetoException();
244 if ( m_bBindingIntrospectee
)
245 throw VetoException();
247 m_bBindingIntrospectee
= true;
248 impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects
.begin(), _rObjects
.end() ) );
249 m_bBindingIntrospectee
= false;
254 Reference
< XDispatch
> SAL_CALL
OPropertyBrowserController::queryDispatch( const URL
& /*URL*/, const OUString
& /*TargetFrameName*/, ::sal_Int32
/*SearchFlags*/ )
256 // we don't have any dispatches at all, right now
257 return Reference
< XDispatch
>();
261 Sequence
< Reference
< XDispatch
> > SAL_CALL
OPropertyBrowserController::queryDispatches( const Sequence
< DispatchDescriptor
>& Requests
)
263 Sequence
< Reference
< XDispatch
> > aReturn
;
264 sal_Int32 nLen
= Requests
.getLength();
265 aReturn
.realloc( nLen
);
267 Reference
< XDispatch
>* pReturn
= aReturn
.getArray();
268 const Reference
< XDispatch
>* pReturnEnd
= aReturn
.getArray() + nLen
;
269 const DispatchDescriptor
* pDescripts
= Requests
.getConstArray();
271 for ( ; pReturn
!= pReturnEnd
; ++ pReturn
, ++pDescripts
)
272 *pReturn
= queryDispatch( pDescripts
->FeatureURL
, pDescripts
->FrameName
, pDescripts
->SearchFlags
);
278 void SAL_CALL
OPropertyBrowserController::initialize( const Sequence
< Any
>& _arguments
)
280 if ( m_bConstructed
)
281 throw AlreadyInitializedException();
283 StlSyntaxSequence
< Any
> arguments( _arguments
);
284 if ( arguments
.empty() )
285 { // constructor: "createDefault()"
286 m_bConstructed
= true;
290 Reference
< XObjectInspectorModel
> xModel
;
291 if ( arguments
.size() == 1 )
292 { // constructor: "createWithModel( XObjectInspectorModel )"
293 if ( !( arguments
[0] >>= xModel
) )
294 throw IllegalArgumentException( OUString(), *this, 0 );
295 createWithModel( xModel
);
299 throw IllegalArgumentException( OUString(), *this, 0 );
303 void OPropertyBrowserController::createWithModel( const Reference
< XObjectInspectorModel
>& _rxModel
)
305 osl_atomic_increment( &m_refCount
);
307 setInspectorModel( _rxModel
);
309 osl_atomic_decrement( &m_refCount
);
311 m_bConstructed
= true;
315 void SAL_CALL
OPropertyBrowserController::attachFrame( const Reference
< XFrame
>& _rxFrame
)
317 SolarMutexGuard aSolarGuard
;
318 ::osl::MutexGuard
aGuard( m_aMutex
);
320 if (_rxFrame
.is() && haveView())
321 throw RuntimeException("Unable to attach to a second frame.",*this);
323 // revoke as focus listener from the old container window
324 stopContainerWindowListening();
333 // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame.
334 // Maybe it is intended to only announce the frame to the controller, and the instance doing this
335 // announcement is responsible for calling setComponent, too.
336 Reference
<XWindow
> xContainerWindow
= m_xFrame
->getContainerWindow();
338 OUString
sUIFile("modules/spropctrlr/ui/formproperties.ui");
339 std::unique_ptr
<weld::Builder
> xBuilder
;
341 if (weld::TransportAsXWindow
* pTunnel
= dynamic_cast<weld::TransportAsXWindow
*>(xContainerWindow
.get()))
343 xBuilder
= Application::CreateBuilder(pTunnel
->getWidget(), sUIFile
);
347 VclPtr
<vcl::Window
> pParentWin
= VCLUnoHelper::GetWindow(xContainerWindow
);
349 throw RuntimeException("The frame is invalid. Unable to extract the container window.",*this);
350 xBuilder
= Application::CreateInterimBuilder(pParentWin
, sUIFile
, true);
353 Construct(xContainerWindow
, std::move(xBuilder
));
355 startContainerWindowListening();
360 sal_Bool SAL_CALL
OPropertyBrowserController::attachModel( const Reference
< XModel
>& _rxModel
)
362 Reference
< XObjectInspectorModel
> xModel( _rxModel
, UNO_QUERY
);
366 setInspectorModel( xModel
);
367 return getInspectorModel() == _rxModel
;
371 bool OPropertyBrowserController::suspendAll_nothrow()
373 // if there is a handle inside its "onInteractivePropertySelection" method,
375 // Normally, we could expect every handler to do this itself, but being
376 // realistic, it's safer to handle this here in general.
377 if ( m_xInteractiveHandler
.is() )
380 m_bSuspendingPropertyHandlers
= true;
381 bool bHandlerVeto
= !suspendPropertyHandlers_nothrow( true );
382 m_bSuspendingPropertyHandlers
= false;
383 return !bHandlerVeto
;
387 bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend
)
389 PropertyHandlerArray aAllHandlers
; // will contain every handler exactly once
390 for (auto const& propertyHandler
: m_aPropertyHandlers
)
392 if ( std::find( aAllHandlers
.begin(), aAllHandlers
.end(), propertyHandler
.second
) != aAllHandlers
.end() )
393 // already visited this particular handler (m_aPropertyHandlers usually contains
394 // the same handler more than once)
396 aAllHandlers
.push_back(propertyHandler
.second
);
399 for (auto const& handler
: aAllHandlers
)
403 if ( !handler
->suspend( _bSuspend
) )
405 // if we're not suspending, but reactivating, ignore the error
408 catch( const Exception
& )
410 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::suspendPropertyHandlers_nothrow" );
417 sal_Bool SAL_CALL
OPropertyBrowserController::suspend( sal_Bool _bSuspend
)
419 ::osl::MutexGuard
aGuard( m_aMutex
);
420 OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" );
423 { // this means a "suspend" is to be "revoked"
424 suspendPropertyHandlers_nothrow( false );
425 // we ourself cannot revoke our suspend
429 if ( !suspendAll_nothrow() )
432 // commit the editor's content
434 getPropertyBox().CommitModified();
437 stopContainerWindowListening();
444 Any SAL_CALL
OPropertyBrowserController::getViewData( )
446 return Any( m_sPageSelection
);
450 void SAL_CALL
OPropertyBrowserController::restoreViewData( const Any
& Data
)
452 OUString sPageSelection
;
453 if ( ( Data
>>= sPageSelection
) && !sPageSelection
.isEmpty() )
455 m_sPageSelection
= sPageSelection
;
456 selectPageFromViewData();
460 Reference
< XModel
> SAL_CALL
OPropertyBrowserController::getModel( )
463 return Reference
< XModel
>();
466 Reference
< XFrame
> SAL_CALL
OPropertyBrowserController::getFrame( )
471 void SAL_CALL
OPropertyBrowserController::dispose()
473 SolarMutexGuard aSolarGuard
;
475 // stop inspecting the current object
476 stopInspection( false );
478 // say our dispose listeners goodbye
479 css::lang::EventObject aEvt
;
480 aEvt
.Source
= static_cast< ::cppu::OWeakObject
* >(this);
481 m_aDisposeListeners
.disposeAndClear(aEvt
);
482 m_aControlObservers
.disposeAndClear(aEvt
);
488 m_xView
->removeEventListener( static_cast< XPropertyChangeListener
* >( this ) );
491 m_aInspectedObjects
.clear();
492 impl_bindToNewModel_nothrow( nullptr );
494 m_xInteractiveHandler
.clear();
498 void SAL_CALL
OPropertyBrowserController::addEventListener( const Reference
< XEventListener
>& _rxListener
)
500 m_aDisposeListeners
.addInterface(_rxListener
);
503 void SAL_CALL
OPropertyBrowserController::removeEventListener( const Reference
< XEventListener
>& _rxListener
)
505 m_aDisposeListeners
.removeInterface(_rxListener
);
508 OUString SAL_CALL
OPropertyBrowserController::getImplementationName( )
510 return "org.openoffice.comp.extensions.ObjectInspector";
513 sal_Bool SAL_CALL
OPropertyBrowserController::supportsService( const OUString
& ServiceName
)
515 return cppu::supportsService(this, ServiceName
);
519 Sequence
< OUString
> SAL_CALL
OPropertyBrowserController::getSupportedServiceNames( )
521 return { "com.sun.star.inspection.ObjectInspector" };
525 void SAL_CALL
OPropertyBrowserController::focusGained( const FocusEvent
& _rSource
)
527 Reference
< XWindow
> xSourceWindow(_rSource
.Source
, UNO_QUERY
);
528 Reference
< XWindow
> xContainerWindow
;
530 xContainerWindow
= m_xFrame
->getContainerWindow();
532 if ( xContainerWindow
.get() == xSourceWindow
.get() )
533 { // our container window got the focus
535 getPropertyBox().GrabFocus();
540 void SAL_CALL
OPropertyBrowserController::focusLost( const FocusEvent
& /*_rSource*/ )
546 void SAL_CALL
OPropertyBrowserController::disposing( const EventObject
& _rSource
)
548 if ( m_xView
.is() && ( m_xView
== _rSource
.Source
) )
555 auto it
= std::find_if(m_aInspectedObjects
.begin(), m_aInspectedObjects
.end(),
556 [&_rSource
](const InterfaceArray::value_type
& rxObj
) { return rxObj
== _rSource
.Source
; });
557 if (it
!= m_aInspectedObjects
.end())
558 m_aInspectedObjects
.erase(it
);
562 IMPL_LINK_NOARG(OPropertyBrowserController
, OnPageActivation
, LinkParamNone
*, void)
564 updateViewDataFromActivePage();
568 void OPropertyBrowserController::updateViewDataFromActivePage()
573 OUString sOldSelection
= m_sPageSelection
;
574 m_sPageSelection
.clear();
576 const sal_uInt16 nCurrentPage
= m_xPropView
->getActivePage();
577 if ( sal_uInt16(-1) != nCurrentPage
)
579 for (auto const& pageId
: m_aPageIds
)
581 if ( nCurrentPage
== pageId
.second
)
583 m_sPageSelection
= pageId
.first
;
589 if ( !m_sPageSelection
.isEmpty() )
590 m_sLastValidPageSelection
= m_sPageSelection
;
591 else if ( !sOldSelection
.isEmpty() )
592 m_sLastValidPageSelection
= sOldSelection
;
596 sal_uInt16
OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString
& _rCategoryName
) const
598 sal_uInt16 nPageId
= sal_uInt16(-1);
599 HashString2Int16::const_iterator pagePos
= m_aPageIds
.find( _rCategoryName
);
600 if ( pagePos
!= m_aPageIds
.end() )
601 nPageId
= pagePos
->second
;
605 void OPropertyBrowserController::selectPageFromViewData()
607 sal_uInt16 nNewPage
= impl_getPageIdForCategory_nothrow( m_sPageSelection
);
609 if ( haveView() && ( nNewPage
!= sal_uInt16(-1) ) )
610 m_xPropView
->activatePage( nNewPage
);
613 updateViewDataFromActivePage();
616 void OPropertyBrowserController::Construct(const Reference
<XWindow
>& rContainerWindow
, std::unique_ptr
<weld::Builder
> xBuilder
)
618 DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!");
619 assert(xBuilder
&& "OPropertyBrowserController::Construct: invalid parent window!");
621 m_xBuilder
= std::move(xBuilder
);
623 m_xPropView
.reset(new OPropertyBrowserView(m_xContext
, *m_xBuilder
));
624 m_xPropView
->setPageActivationHandler(LINK(this, OPropertyBrowserController
, OnPageActivation
));
626 // add as dispose listener for our view. The view is disposed by the frame we're plugged into,
627 // and this disposal _deletes_ the view, so it would be deadly if we use our m_xPropView member
629 m_xView
= rContainerWindow
;
631 m_xView
->addEventListener( static_cast< XPropertyChangeListener
* >( this ) );
633 getPropertyBox().SetLineListener(this);
634 getPropertyBox().SetControlObserver(this);
635 impl_initializeView_nothrow();
638 void SAL_CALL
OPropertyBrowserController::propertyChange( const PropertyChangeEvent
& _rEvent
)
640 if ( _rEvent
.Source
== m_xModel
)
642 if ( _rEvent
.PropertyName
== "IsReadOnly" )
643 // this is a huge cudgel, admitted.
644 // The problem is that in case we were previously read-only, all our controls
645 // were created read-only, too. We cannot simply switch them to not-read-only.
646 // Even if they had an API for this, we do not know whether they were
647 // originally created read-only, or if they are read-only just because
649 impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects
) );
653 if ( m_sCommittingProperty
== _rEvent
.PropertyName
)
659 Any
aNewValue( _rEvent
.NewValue
);
660 if ( impl_hasPropertyHandlerFor_nothrow( _rEvent
.PropertyName
) )
662 // forward the new value to the property box, to reflect the change in the UI
663 aNewValue
= impl_getPropertyValue_throw( _rEvent
.PropertyName
);
665 // check whether the state is ambiguous. This is interesting in case we display the properties
666 // for multiple objects at once: In this case, we'll get a notification from one of the objects,
667 // but need to care for the "composed" value, which can be "ambiguous".
668 PropertyHandlerRef
xHandler( impl_getHandlerForProperty_throw( _rEvent
.PropertyName
), UNO_SET_THROW
);
669 PropertyState
ePropertyState( xHandler
->getPropertyState( _rEvent
.PropertyName
) );
670 bool bAmbiguousValue
= ( PropertyState_AMBIGUOUS_VALUE
== ePropertyState
);
672 getPropertyBox().SetPropertyValue( _rEvent
.PropertyName
, aNewValue
, bAmbiguousValue
);
675 // if it's an actuating property, then update the UI for any dependent
677 if ( impl_isActuatingProperty_nothrow( _rEvent
.PropertyName
) )
678 impl_broadcastPropertyChange_nothrow( _rEvent
.PropertyName
, aNewValue
, _rEvent
.OldValue
, false );
681 Reference
< XPropertyControl
> SAL_CALL
OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType
, sal_Bool bCreateReadOnly
)
683 ::osl::MutexGuard
aGuard( m_aMutex
);
685 Reference
< XPropertyControl
> xControl
;
688 bCreateReadOnly
|= impl_isReadOnlyModel_throw() ? 1 : 0;
690 switch ( ControlType
)
692 case PropertyControlType::MultiLineTextField
:
693 case PropertyControlType::StringListField
:
695 bool bMultiLineTextField
= ControlType
== PropertyControlType::MultiLineTextField
;
696 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/multiline.ui", m_xContext
));
697 auto pContainer
= xBuilder
->weld_container("multiline");
698 rtl::Reference
<OMultilineEditControl
> pControl
= new OMultilineEditControl(std::move(pContainer
), std::move(xBuilder
),
699 bMultiLineTextField
? eMultiLineText
: eStringList
, bCreateReadOnly
);
700 pControl
->SetModifyHandler();
705 case PropertyControlType::ListBox
:
707 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/listbox.ui", m_xContext
));
708 auto pComboBox
= xBuilder
->weld_combo_box("listbox");
709 rtl::Reference
<OListboxControl
> pControl
= new OListboxControl(std::move(pComboBox
), std::move(xBuilder
), bCreateReadOnly
);
710 pControl
->SetModifyHandler();
715 case PropertyControlType::ComboBox
:
717 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/combobox.ui", m_xContext
));
718 auto pComboBox
= xBuilder
->weld_combo_box("combobox");
719 rtl::Reference
<OComboboxControl
> pControl
= new OComboboxControl(std::move(pComboBox
), std::move(xBuilder
), bCreateReadOnly
);
720 pControl
->SetModifyHandler();
725 case PropertyControlType::TextField
:
726 case PropertyControlType::CharacterField
:
728 bool bCharacterField
= ControlType
== PropertyControlType::CharacterField
;
729 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/textfield.ui", m_xContext
));
730 auto pEntry
= xBuilder
->weld_entry("textfield");
731 rtl::Reference
<OEditControl
> pControl
= new OEditControl(std::move(pEntry
), std::move(xBuilder
), bCharacterField
, bCreateReadOnly
);
732 pControl
->SetModifyHandler();
737 case PropertyControlType::NumericField
:
739 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/numericfield.ui", m_xContext
));
740 auto pSpinButton
= xBuilder
->weld_metric_spin_button("numericfield", FieldUnit::NONE
);
741 rtl::Reference
<ONumericControl
> pControl
= new ONumericControl(std::move(pSpinButton
), std::move(xBuilder
), bCreateReadOnly
);
742 pControl
->SetModifyHandler();
747 case PropertyControlType::DateTimeField
:
749 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/datetimefield.ui", m_xContext
));
750 auto pContainer
= xBuilder
->weld_container("datetimefield");
751 rtl::Reference
<ODateTimeControl
> pControl
= new ODateTimeControl(std::move(pContainer
), std::move(xBuilder
), bCreateReadOnly
);
752 pControl
->SetModifyHandler();
757 case PropertyControlType::DateField
:
759 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/datefield.ui", m_xContext
));
760 auto pContainer
= xBuilder
->weld_container("datefield");
761 rtl::Reference
<ODateControl
> pControl
= new ODateControl(std::move(pContainer
), std::move(xBuilder
), bCreateReadOnly
);
762 pControl
->SetModifyHandler();
767 case PropertyControlType::TimeField
:
769 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/timefield.ui", m_xContext
));
770 auto pTimeSpinButton
= xBuilder
->weld_formatted_spin_button("timefield");
771 rtl::Reference
<OTimeControl
> pControl
= new OTimeControl(std::move(pTimeSpinButton
), std::move(xBuilder
), bCreateReadOnly
);
772 pControl
->SetModifyHandler();
777 case PropertyControlType::ColorListBox
:
779 auto lambda
= [this]{ return PropertyHandlerHelper::getDialogParentFrame(m_xContext
); };
780 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/colorlistbox.ui", m_xContext
));
781 auto pMenuButton
= xBuilder
->weld_menu_button("colorlistbox");
782 rtl::Reference
<OColorControl
> pControl
= new OColorControl(std::make_unique
<ColorListBox
>(std::move(pMenuButton
), lambda
), std::move(xBuilder
), bCreateReadOnly
);
783 pControl
->SetModifyHandler();
788 case PropertyControlType::HyperlinkField
:
790 std::unique_ptr
<weld::Builder
> xBuilder(PropertyHandlerHelper::makeBuilder("modules/spropctrlr/ui/hyperlinkfield.ui", m_xContext
));
791 auto pContainer
= xBuilder
->weld_container("hyperlinkfield");
792 rtl::Reference
<OHyperlinkControl
> pControl
= new OHyperlinkControl(std::move(pContainer
), std::move(xBuilder
), bCreateReadOnly
);
793 pControl
->SetModifyHandler();
799 throw IllegalArgumentException( OUString(), *this, 1 );
806 void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn
)
808 for (auto const& inspectedObject
: m_aInspectedObjects
)
812 Reference
< XComponent
> xComp( inspectedObject
, UNO_QUERY
);
816 xComp
->addEventListener( static_cast< XPropertyChangeListener
* >( this ) );
818 xComp
->removeEventListener( static_cast< XPropertyChangeListener
* >( this ) );
821 catch( const Exception
& )
823 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
829 void OPropertyBrowserController::stopInspection( bool _bCommitModified
)
833 if ( _bCommitModified
)
834 // commit the editor's content
835 getPropertyBox().CommitModified();
837 // hide the property box so that it does not flicker
838 getPropertyBox().Hide();
840 // clear the property box
841 getPropertyBox().ClearAll();
844 // destroy the view first
848 for (auto const& pageId
: m_aPageIds
)
849 getPropertyBox().RemovePage( pageId
.second
);
850 clearContainer( m_aPageIds
);
853 clearContainer( m_aProperties
);
855 // de-register as dispose-listener from our inspected objects
856 impl_toggleInspecteeListening_nothrow( false );
858 // handlers are obsolete, so is our "composer" for their UI requests
859 if (m_pUIRequestComposer
)
860 m_pUIRequestComposer
->dispose();
861 m_pUIRequestComposer
.reset();
863 // clean up the property handlers
864 PropertyHandlerArray aAllHandlers
; // will contain every handler exactly once
865 for (auto const& propertyHandler
: m_aPropertyHandlers
)
866 if ( std::find( aAllHandlers
.begin(), aAllHandlers
.end(), propertyHandler
.second
) == aAllHandlers
.end() )
867 aAllHandlers
.push_back( propertyHandler
.second
);
869 for (auto const& handler
: aAllHandlers
)
873 handler
->removePropertyChangeListener( this );
876 catch( const DisposedException
& )
879 catch( const Exception
& )
881 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
885 clearContainer( m_aPropertyHandlers
);
886 clearContainer( m_aDependencyHandlers
);
890 bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString
& _rPropertyName
) const
892 PropertyHandlerRepository::const_iterator handlerPos
= m_aPropertyHandlers
.find( _rPropertyName
);
893 return ( handlerPos
!= m_aPropertyHandlers
.end() );
897 OPropertyBrowserController::PropertyHandlerRef
const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString
& _rPropertyName
) const
899 PropertyHandlerRepository::const_iterator handlerPos
= m_aPropertyHandlers
.find( _rPropertyName
);
900 if ( handlerPos
== m_aPropertyHandlers
.end() )
901 throw RuntimeException();
902 return handlerPos
->second
;
906 Any
OPropertyBrowserController::impl_getPropertyValue_throw( const OUString
& _rPropertyName
)
908 PropertyHandlerRef handler
= impl_getHandlerForProperty_throw( _rPropertyName
);
909 return handler
->getPropertyValue( _rPropertyName
);
913 void OPropertyBrowserController::impl_rebindToInspectee_nothrow( InterfaceArray
&& _rObjects
)
917 // stop inspecting the old object(s)
918 stopInspection( true );
920 // inspect the new object(s)
921 m_aInspectedObjects
= std::move(_rObjects
);
924 // update the user interface
928 catch(const Exception
&)
930 TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
935 void OPropertyBrowserController::doInspection()
940 // obtain the properties of the object
941 std::vector
< Property
> aProperties
;
943 PropertyHandlerArray aPropertyHandlers
;
944 getPropertyHandlers( m_aInspectedObjects
, aPropertyHandlers
);
946 PropertyHandlerArray::iterator
aHandler( aPropertyHandlers
.begin() );
947 while ( aHandler
!= aPropertyHandlers
.end() )
949 DBG_ASSERT( aHandler
->get(), "OPropertyBrowserController::doInspection: invalid handler!" );
951 StlSyntaxSequence
< Property
> aThisHandlersProperties( (*aHandler
)->getSupportedProperties() );
953 if ( aThisHandlersProperties
.empty() )
955 // this handler doesn't know anything about the current inspectee -> ignore it
956 (*aHandler
)->dispose();
957 aHandler
= aPropertyHandlers
.erase( aHandler
);
961 // append these properties to our "all properties" array
962 aProperties
.reserve( std::max
<size_t>(aProperties
.size() + aThisHandlersProperties
.size(), aProperties
.size() * 2) );
963 for (const auto & aThisHandlersProperty
: aThisHandlersProperties
)
965 auto noPrevious
= std::none_of(
968 FindPropertyByName( aThisHandlersProperty
.Name
)
972 aProperties
.push_back( aThisHandlersProperty
);
976 // there already was another (previous) handler which supported this property.
977 // Don't add it to aProperties, again.
979 // Also, ensure that handlers which previously expressed interest in *changes*
980 // of this property are not notified.
981 // This is 'cause we have a new handler which is responsible for this property,
982 // which means it can give it a completely different meaning than the previous
983 // handler for this property is prepared for.
984 std::pair
< PropertyHandlerMultiRepository::iterator
, PropertyHandlerMultiRepository::iterator
>
985 aDepHandlers
= m_aDependencyHandlers
.equal_range( aThisHandlersProperty
.Name
);
986 m_aDependencyHandlers
.erase( aDepHandlers
.first
, aDepHandlers
.second
);
989 // determine the superseded properties
990 StlSyntaxSequence
< OUString
> aSupersededByThisHandler( (*aHandler
)->getSupersededProperties() );
991 for (const auto & superseded
: aSupersededByThisHandler
)
993 std::vector
< Property
>::iterator existent
= std::find_if(
996 FindPropertyByName( superseded
)
998 if ( existent
!= aProperties
.end() )
999 // one of the properties superseded by this handler was supported by a previous
1001 aProperties
.erase( existent
);
1004 // be notified of changes which this handler is responsible for
1005 (*aHandler
)->addPropertyChangeListener( this );
1007 // remember this handler for every of the properties which it is responsible
1009 for (const auto & aThisHandlersProperty
: aThisHandlersProperties
)
1011 m_aPropertyHandlers
[ aThisHandlersProperty
.Name
] = *aHandler
;
1012 // note that this implies that if two handlers support the same property,
1016 // see if the handler expresses interest in any actuating properties
1017 StlSyntaxSequence
< OUString
> aInterestingActuations( (*aHandler
)->getActuatingProperties() );
1018 for (const auto & aInterestingActuation
: aInterestingActuations
)
1020 m_aDependencyHandlers
.emplace( aInterestingActuation
, *aHandler
);
1026 // create a new composer for UI requests coming from the handlers
1027 m_pUIRequestComposer
.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) );
1029 // sort the properties by relative position, as indicated by the model
1031 for (auto const& sourceProps
: aProperties
)
1033 sal_Int32 nRelativePropertyOrder
= nPos
;
1034 if ( m_xModel
.is() )
1035 nRelativePropertyOrder
= m_xModel
->getPropertyOrderIndex( sourceProps
.Name
);
1036 m_aProperties
.emplace(nRelativePropertyOrder
, sourceProps
);
1040 // be notified when one of our inspectees dies
1041 impl_toggleInspecteeListening_nothrow( true );
1043 catch(const Exception
&)
1045 TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
1050 css::awt::Size SAL_CALL
OPropertyBrowserController::getMinimumSize()
1052 css::awt::Size aSize
;
1054 return m_xPropView
->getMinimumSize();
1060 css::awt::Size SAL_CALL
OPropertyBrowserController::getPreferredSize()
1062 return getMinimumSize();
1066 css::awt::Size SAL_CALL
OPropertyBrowserController::calcAdjustedSize( const css::awt::Size
& _rNewSize
)
1068 awt::Size aMinSize
= getMinimumSize( );
1069 awt::Size
aAdjustedSize( _rNewSize
);
1070 if ( aAdjustedSize
.Width
< aMinSize
.Width
)
1071 aAdjustedSize
.Width
= aMinSize
.Width
;
1072 if ( aAdjustedSize
.Height
< aMinSize
.Height
)
1073 aAdjustedSize
.Height
= aMinSize
.Height
;
1074 return aAdjustedSize
;
1078 void OPropertyBrowserController::describePropertyLine( const Property
& _rProperty
, OLineDescriptor
& _rDescriptor
)
1082 PropertyHandlerRepository::const_iterator handler
= m_aPropertyHandlers
.find( _rProperty
.Name
);
1083 if ( handler
== m_aPropertyHandlers
.end() )
1084 throw RuntimeException(); // caught below
1086 _rDescriptor
.assignFrom( handler
->second
->describePropertyLine( _rProperty
.Name
, this ) );
1089 _rDescriptor
.xPropertyHandler
= handler
->second
;
1090 _rDescriptor
.sName
= _rProperty
.Name
;
1091 _rDescriptor
.aValue
= _rDescriptor
.xPropertyHandler
->getPropertyValue( _rProperty
.Name
);
1093 if ( _rDescriptor
.DisplayName
.isEmpty() )
1096 SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
1097 <<_rProperty
.Name
<< "'!" );
1099 _rDescriptor
.DisplayName
= _rProperty
.Name
;
1102 PropertyState
ePropertyState( _rDescriptor
.xPropertyHandler
->getPropertyState( _rProperty
.Name
) );
1103 if ( PropertyState_AMBIGUOUS_VALUE
== ePropertyState
)
1105 _rDescriptor
.bUnknownValue
= true;
1106 _rDescriptor
.aValue
.clear();
1109 _rDescriptor
.bReadOnly
= impl_isReadOnlyModel_throw();
1111 // for ui-testing try and distinguish different instances of the controls
1112 auto xWindow
= _rDescriptor
.Control
->getControlWindow();
1113 if (weld::TransportAsXWindow
* pTunnel
= dynamic_cast<weld::TransportAsXWindow
*>(xWindow
.get()))
1115 weld::Widget
* m_pControlWindow
= pTunnel
->getWidget();
1116 if (m_pControlWindow
)
1117 m_pControlWindow
->set_buildable_name(m_pControlWindow
->get_buildable_name() + "-" + _rDescriptor
.DisplayName
);
1121 catch( const Exception
& )
1123 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine" );
1128 void OPropertyBrowserController::impl_buildCategories_throw()
1130 OSL_PRECOND( m_aPageIds
.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" );
1132 StlSyntaxSequence
< PropertyCategoryDescriptor
> aCategories
;
1133 if ( m_xModel
.is() )
1134 aCategories
= StlSyntaxSequence
< PropertyCategoryDescriptor
>(m_xModel
->describeCategories());
1136 for (auto const& category
: aCategories
)
1138 OSL_ENSURE( m_aPageIds
.find( category
.ProgrammaticName
) == m_aPageIds
.end(),
1139 "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" );
1141 m_aPageIds
[ category
.ProgrammaticName
] =
1142 getPropertyBox().AppendPage( category
.UIName
, HelpIdUrl::getHelpId( category
.HelpURL
) );
1147 void OPropertyBrowserController::UpdateUI()
1152 // too early, will return later
1155 // create our tab pages
1156 impl_buildCategories_throw();
1157 // (and allow for pages to be actually unused)
1158 std::set
< sal_uInt16
> aUsedPages
;
1160 // when building the UI below, remember which properties are actuating,
1161 // to allow for an initial actuatingPropertyChanged call
1162 std::vector
< OUString
> aActuatingProperties
;
1163 std::vector
< Any
> aActuatingPropertyValues
;
1165 // ask the handlers to describe the property UI, and insert the resulting
1166 // entries into our list boxes
1167 for (auto const& property
: m_aProperties
)
1169 OLineDescriptor aDescriptor
;
1170 describePropertyLine( property
.second
, aDescriptor
);
1172 bool bIsActuatingProperty
= impl_isActuatingProperty_nothrow( property
.second
.Name
);
1174 SAL_WARN_IF( aDescriptor
.Category
.isEmpty(), "extensions.propctrlr",
1175 "OPropertyBrowserController::UpdateUI: empty category provided for property '"
1176 << property
.second
.Name
<< "'!");
1177 // finally insert this property control
1178 sal_uInt16 nTargetPageId
= impl_getPageIdForCategory_nothrow( aDescriptor
.Category
);
1179 if ( nTargetPageId
== sal_uInt16(-1) )
1181 // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide
1182 // any category information of its own. In this case, we have a fallback ...
1183 m_aPageIds
[ aDescriptor
.Category
] =
1184 getPropertyBox().AppendPage(aDescriptor
.Category
, {});
1185 nTargetPageId
= impl_getPageIdForCategory_nothrow( aDescriptor
.Category
);
1188 getPropertyBox().InsertEntry( aDescriptor
, nTargetPageId
);
1189 aUsedPages
.insert( nTargetPageId
);
1191 // if it's an actuating property, remember it
1192 if ( bIsActuatingProperty
)
1194 aActuatingProperties
.push_back( property
.second
.Name
);
1195 aActuatingPropertyValues
.push_back( impl_getPropertyValue_throw( property
.second
.Name
) );
1199 // update any dependencies for the actuating properties which we encountered
1201 std::vector
< Any
>::const_iterator aPropertyValue
= aActuatingPropertyValues
.begin();
1202 for (auto const& actuatingProperty
: aActuatingProperties
)
1204 impl_broadcastPropertyChange_nothrow( actuatingProperty
, *aPropertyValue
, *aPropertyValue
, true );
1209 // remove any unused pages (which we did not encounter properties for)
1210 HashString2Int16 aSurvivingPageIds
;
1211 for (auto const& pageId
: m_aPageIds
)
1213 if ( aUsedPages
.find( pageId
.second
) == aUsedPages
.end() )
1214 getPropertyBox().RemovePage( pageId
.second
);
1216 aSurvivingPageIds
.insert(pageId
);
1218 m_aPageIds
.swap( aSurvivingPageIds
);
1220 getPropertyBox().Show();
1222 // activate the first page
1223 if ( !m_aPageIds
.empty() )
1225 Sequence
< PropertyCategoryDescriptor
> aCategories( m_xModel
->describeCategories() );
1226 if ( aCategories
.hasElements() )
1227 m_xPropView
->activatePage( m_aPageIds
[ aCategories
[0].ProgrammaticName
] );
1229 // allowed: if we default-created the pages ...
1230 m_xPropView
->activatePage( m_aPageIds
.begin()->second
);
1233 // activate the previously active page (if possible)
1234 if ( !m_sLastValidPageSelection
.isEmpty() )
1235 m_sPageSelection
= m_sLastValidPageSelection
;
1236 selectPageFromViewData();
1238 catch( const Exception
& )
1240 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1245 void OPropertyBrowserController::Clicked( const OUString
& _rName
, bool _bPrimary
)
1249 // since the browse buttons do not get the focus when clicked with the mouse,
1250 // we need to commit the changes in the current property field
1251 getPropertyBox().CommitModified();
1253 PropertyHandlerRepository::const_iterator handler
= m_aPropertyHandlers
.find( _rName
);
1254 DBG_ASSERT( handler
!= m_aPropertyHandlers
.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" );
1256 ComposedUIAutoFireGuard
aAutoFireGuard( *m_pUIRequestComposer
);
1259 m_xInteractiveHandler
= handler
->second
;
1260 InteractiveSelectionResult eResult
=
1261 handler
->second
->onInteractivePropertySelection( _rName
, _bPrimary
, aData
,
1262 m_pUIRequestComposer
->getUIForPropertyHandler( handler
->second
) );
1266 case InteractiveSelectionResult_Cancelled
:
1267 case InteractiveSelectionResult_Success
:
1268 // okay, nothing to do
1270 case InteractiveSelectionResult_ObtainedValue
:
1271 handler
->second
->setPropertyValue( _rName
, aData
);
1273 case InteractiveSelectionResult_Pending
:
1274 // also okay, we expect that the handler has disabled the UI as necessary
1277 OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
1281 catch (const Exception
&)
1283 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1285 m_xInteractiveHandler
= nullptr;
1289 bool OPropertyBrowserController::hasPropertyByName( const OUString
& _rName
)
1291 for (auto const& property
: m_aProperties
)
1292 if ( property
.second
.Name
== _rName
)
1298 void OPropertyBrowserController::Commit( const OUString
& rName
, const Any
& _rValue
)
1302 OUString sPlcHolder
= PcrRes(RID_EMBED_IMAGE_PLACEHOLDER
);
1303 bool bIsPlaceHolderValue
= false;
1305 if ( rName
== PROPERTY_IMAGE_URL
)
1307 // if the prop value is the PlaceHolder
1311 if ( sVal
== sPlcHolder
)
1312 bIsPlaceHolderValue
= true;
1314 m_sCommittingProperty
= rName
;
1316 bool bIsActuatingProperty
= impl_isActuatingProperty_nothrow( rName
);
1319 if ( bIsActuatingProperty
)
1320 aOldValue
= impl_getPropertyValue_throw( rName
);
1322 // do we have a dedicated handler for this property, which we can delegate some tasks to?
1323 PropertyHandlerRef handler
= impl_getHandlerForProperty_throw( rName
);
1326 // set the value ( only if it's not a placeholder )
1327 if ( !bIsPlaceHolderValue
)
1328 handler
->setPropertyValue( rName
, _rValue
);
1331 // re-retrieve the value
1332 Any aNormalizedValue
= handler
->getPropertyValue( rName
);
1334 // care for any inter-property dependencies
1335 if ( bIsActuatingProperty
)
1336 impl_broadcastPropertyChange_nothrow( rName
, aNormalizedValue
, aOldValue
, false );
1338 // and display it again. This ensures proper formatting
1339 getPropertyBox().SetPropertyValue( rName
, aNormalizedValue
, false );
1341 catch(const PropertyVetoException
& eVetoException
)
1343 std::unique_ptr
<weld::MessageDialog
> xInfoBox(Application::CreateMessageDialog(m_xPropView
->getPropertyBox().getWidget(),
1344 VclMessageType::Info
, VclButtonsType::Ok
,
1345 eVetoException
.Message
));
1347 PropertyHandlerRef handler
= impl_getHandlerForProperty_throw( rName
);
1348 Any aNormalizedValue
= handler
->getPropertyValue( rName
);
1349 getPropertyBox().SetPropertyValue( rName
, aNormalizedValue
, false );
1351 catch(const Exception
&)
1353 TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
1356 m_sCommittingProperty
.clear();
1360 void OPropertyBrowserController::focusGained( const Reference
< XPropertyControl
>& Control
)
1362 m_aControlObservers
.notifyEach( &XPropertyControlObserver::focusGained
, Control
);
1366 void OPropertyBrowserController::valueChanged( const Reference
< XPropertyControl
>& Control
)
1368 m_aControlObservers
.notifyEach( &XPropertyControlObserver::valueChanged
, Control
);
1374 Reference
< XPropertyHandler
> lcl_createHandler( const Reference
<XComponentContext
>& _rContext
, const Any
& _rFactoryDescriptor
)
1376 Reference
< XPropertyHandler
> xHandler
;
1378 OUString sServiceName
;
1379 Reference
< XSingleServiceFactory
> xServiceFac
;
1380 Reference
< XSingleComponentFactory
> xComponentFac
;
1382 if ( _rFactoryDescriptor
>>= sServiceName
)
1383 xHandler
.set( _rContext
->getServiceManager()->createInstanceWithContext( sServiceName
, _rContext
), UNO_QUERY
);
1384 else if ( _rFactoryDescriptor
>>= xServiceFac
)
1385 xHandler
.set(xServiceFac
->createInstance(), css::uno::UNO_QUERY
);
1386 else if ( _rFactoryDescriptor
>>= xComponentFac
)
1387 xHandler
.set(xComponentFac
->createInstanceWithContext( _rContext
), css::uno::UNO_QUERY
);
1388 OSL_ENSURE(xHandler
.is(),"lcl_createHandler: Can not create handler");
1394 void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray
& _rObjects
, PropertyHandlerArray
& _rHandlers
)
1396 _rHandlers
.resize( 0 );
1397 if ( _rObjects
.empty() )
1400 Sequence
< Any
> aHandlerFactories
;
1401 if ( m_xModel
.is() )
1402 aHandlerFactories
= m_xModel
->getHandlerFactories();
1404 for ( auto const & handlerFactory
: std::as_const(aHandlerFactories
) )
1406 if ( _rObjects
.size() == 1 )
1407 { // we're inspecting only one object -> one handler
1408 Reference
< XPropertyHandler
> xHandler( lcl_createHandler( m_xContext
, handlerFactory
) );
1409 if ( xHandler
.is() )
1411 xHandler
->inspect( _rObjects
[0] );
1412 _rHandlers
.push_back( xHandler
);
1417 // create a single handler for every single object
1418 std::vector
< Reference
< XPropertyHandler
> > aSingleHandlers( _rObjects
.size() );
1419 std::vector
< Reference
< XPropertyHandler
> >::iterator pHandler
= aSingleHandlers
.begin();
1421 for (auto const& elem
: _rObjects
)
1423 *pHandler
= lcl_createHandler( m_xContext
, handlerFactory
);
1424 if ( pHandler
->is() )
1426 (*pHandler
)->inspect(elem
);
1430 aSingleHandlers
.resize( pHandler
- aSingleHandlers
.begin() );
1432 // then create a handler which composes information out of those single handlers
1433 if ( !aSingleHandlers
.empty() )
1434 _rHandlers
.push_back( new PropertyComposer( std::move(aSingleHandlers
) ) );
1438 // note that the handlers will not be used by our caller, if they indicate that there are no
1439 // properties they feel responsible for
1443 bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString
& _rName
, OrderedPropertyMap::const_iterator
* _pProperty
)
1445 OrderedPropertyMap::const_iterator search
= std::find_if(m_aProperties
.begin(), m_aProperties
.end(),
1446 [&_rName
](const OrderedPropertyMap::value_type
& rEntry
) { return rEntry
.second
.Name
== _rName
; });
1448 *_pProperty
= search
;
1449 return ( search
!= m_aProperties
.end() );
1453 void OPropertyBrowserController::rebuildPropertyUI( const OUString
& _rPropertyName
)
1455 ::osl::MutexGuard
aGuard( m_aMutex
);
1457 throw RuntimeException();
1459 OrderedPropertyMap::const_iterator propertyPos
;
1460 if ( !impl_findObjectProperty_nothrow( _rPropertyName
, &propertyPos
) )
1463 OLineDescriptor aDescriptor
;
1466 describePropertyLine( propertyPos
->second
, aDescriptor
);
1468 catch( const Exception
& )
1470 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::rebuildPropertyUI" );
1473 getPropertyBox().ChangeEntry( aDescriptor
);
1477 void OPropertyBrowserController::enablePropertyUI( const OUString
& _rPropertyName
, sal_Bool _bEnable
)
1479 ::osl::MutexGuard
aGuard( m_aMutex
);
1481 throw RuntimeException();
1483 if ( !impl_findObjectProperty_nothrow( _rPropertyName
) )
1486 getPropertyBox().EnablePropertyLine( _rPropertyName
, _bEnable
);
1490 void OPropertyBrowserController::enablePropertyUIElements( const OUString
& _rPropertyName
, sal_Int16 _nElements
, sal_Bool _bEnable
)
1492 ::osl::MutexGuard
aGuard( m_aMutex
);
1494 throw RuntimeException();
1496 if ( !impl_findObjectProperty_nothrow( _rPropertyName
) )
1499 getPropertyBox().EnablePropertyControls( _rPropertyName
, _nElements
, _bEnable
);
1503 void OPropertyBrowserController::showPropertyUI( const OUString
& _rPropertyName
)
1505 ::osl::MutexGuard
aGuard( m_aMutex
);
1507 throw RuntimeException();
1509 // look up the property in our object properties
1510 OrderedPropertyMap::const_iterator propertyPos
;
1511 if ( !impl_findObjectProperty_nothrow( _rPropertyName
, &propertyPos
) )
1514 if ( getPropertyBox().GetPropertyPos( _rPropertyName
) != EDITOR_LIST_ENTRY_NOTFOUND
)
1516 rebuildPropertyUI( _rPropertyName
);
1520 OLineDescriptor aDescriptor
;
1521 describePropertyLine( propertyPos
->second
, aDescriptor
);
1523 // look for the position to insert the property
1525 // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work
1526 // only on the current page. This implies that it's impossible to use this method here
1527 // to show property lines which are *not* on the current page.
1528 // This is sufficient for now, but should be changed in the future.
1530 // by definition, the properties in m_aProperties are in the order in which they appear in the UI
1531 // So all we need is a predecessor of pProperty in m_aProperties
1532 sal_uInt16 nUIPos
= EDITOR_LIST_ENTRY_NOTFOUND
;
1535 if ( propertyPos
!= m_aProperties
.begin() )
1537 nUIPos
= getPropertyBox().GetPropertyPos( propertyPos
->second
.Name
);
1539 while ( ( nUIPos
== EDITOR_LIST_ENTRY_NOTFOUND
) && ( propertyPos
!= m_aProperties
.begin() ) );
1541 if ( nUIPos
== EDITOR_LIST_ENTRY_NOTFOUND
)
1542 // insert at the very top
1545 // insert right after the predecessor we found
1548 getPropertyBox().InsertEntry(
1549 aDescriptor
, impl_getPageIdForCategory_nothrow( aDescriptor
.Category
), nUIPos
);
1553 void OPropertyBrowserController::hidePropertyUI( const OUString
& _rPropertyName
)
1555 ::osl::MutexGuard
aGuard( m_aMutex
);
1557 throw RuntimeException();
1559 if ( !impl_findObjectProperty_nothrow( _rPropertyName
) )
1562 getPropertyBox().RemoveEntry( _rPropertyName
);
1566 void OPropertyBrowserController::showCategory( const OUString
& rCategory
, sal_Bool bShow
)
1568 ::osl::MutexGuard
aGuard( m_aMutex
);
1570 throw RuntimeException();
1572 sal_uInt16 nPageId
= impl_getPageIdForCategory_nothrow( rCategory
);
1573 OSL_ENSURE( nPageId
!= sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" );
1575 getPropertyBox().ShowPropertyPage( nPageId
, bShow
);
1579 Reference
< XPropertyControl
> SAL_CALL
OPropertyBrowserController::getPropertyControl( const OUString
& _rPropertyName
)
1581 ::osl::MutexGuard
aGuard( m_aMutex
);
1583 throw RuntimeException();
1585 Reference
< XPropertyControl
> xControl( getPropertyBox().GetPropertyControl( _rPropertyName
) );
1590 void SAL_CALL
OPropertyBrowserController::registerControlObserver( const Reference
< XPropertyControlObserver
>& Observer
)
1592 m_aControlObservers
.addInterface( Observer
);
1596 void SAL_CALL
OPropertyBrowserController::revokeControlObserver( const Reference
< XPropertyControlObserver
>& Observer
)
1598 m_aControlObservers
.removeInterface( Observer
);
1602 void SAL_CALL
OPropertyBrowserController::setHelpSectionText( const OUString
& _rHelpText
)
1604 SolarMutexGuard aSolarGuard
;
1605 ::osl::MutexGuard
aGuard( m_aMutex
);
1608 throw DisposedException();
1610 if ( !getPropertyBox().HasHelpSection() )
1611 throw NoSupportException();
1613 getPropertyBox().SetHelpText( _rHelpText
);
1617 void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString
& _rPropertyName
, const Any
& _rNewValue
, const Any
& _rOldValue
, bool _bFirstTimeInit
) const
1619 // are there one or more handlers which are interested in the actuation?
1620 std::pair
< PropertyHandlerMultiRepository::const_iterator
, PropertyHandlerMultiRepository::const_iterator
> aInterestedHandlers
=
1621 m_aDependencyHandlers
.equal_range( _rPropertyName
);
1622 if ( aInterestedHandlers
.first
== aInterestedHandlers
.second
)
1623 // none of our handlers is interested in this
1626 ComposedUIAutoFireGuard
aAutoFireGuard( *m_pUIRequestComposer
);
1629 // collect the responses from all interested handlers
1630 PropertyHandlerMultiRepository::const_iterator handler
= aInterestedHandlers
.first
;
1631 while ( handler
!= aInterestedHandlers
.second
)
1633 handler
->second
->actuatingPropertyChanged( _rPropertyName
, _rNewValue
, _rOldValue
,
1634 m_pUIRequestComposer
->getUIForPropertyHandler( handler
->second
),
1639 catch( const Exception
& )
1641 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1648 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1649 extensions_propctrlr_OPropertyBrowserController_get_implementation(
1650 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
1652 return cppu::acquire(new pcr::OPropertyBrowserController(context
));
1655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */