Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / extensions / source / propctrlr / propcontroller.cxx
blob739f83aa4a6333efc53a8075e153614c305da753
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 .
20 #include "pcrservices.hxx"
21 #include "propcontroller.hxx"
22 #include "pcrstrings.hxx"
23 #include "standardcontrol.hxx"
24 #include "linedescriptor.hxx"
25 #include <strings.hrc>
26 #include "propertyeditor.hxx"
27 #include "modulepcr.hxx"
28 #include "formstrings.hxx"
29 #include "formmetadata.hxx"
30 #include "formbrowsertools.hxx"
31 #include "propertycomposer.hxx"
33 #include <com/sun/star/awt/XWindow.hpp>
34 #include <com/sun/star/lang/NoSupportException.hpp>
35 #include <com/sun/star/util/XCloseable.hpp>
36 #include <com/sun/star/inspection/PropertyControlType.hpp>
37 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
38 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
39 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 #include <com/sun/star/util/VetoException.hpp>
41 #include <tools/debug.hxx>
42 #include <tools/diagnose_ex.h>
43 #include <comphelper/types.hxx>
44 #include <toolkit/awt/vclxwindow.hxx>
45 #include <toolkit/helper/vclunohelper.hxx>
46 #include <comphelper/property.hxx>
47 #include <vcl/weld.hxx>
48 #include <vcl/svapp.hxx>
49 #include <osl/mutex.hxx>
50 #include <cppuhelper/queryinterface.hxx>
51 #include <cppuhelper/component_context.hxx>
52 #include <cppuhelper/exc_hlp.hxx>
53 #include <cppuhelper/supportsservice.hxx>
55 #include <algorithm>
56 #include <functional>
57 #include <sal/macros.h>
58 #include <sal/log.hxx>
61 // !!! outside the namespace !!!
62 extern "C" void createRegistryInfo_OPropertyBrowserController()
64 ::pcr::OAutoRegistration< ::pcr::OPropertyBrowserController > aAutoRegistration;
68 namespace pcr
72 using namespace ::com::sun::star;
73 using namespace ::com::sun::star::uno;
74 using namespace ::com::sun::star::awt;
75 using namespace ::com::sun::star::form;
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::script;
78 using namespace ::com::sun::star::lang;
79 using namespace ::com::sun::star::container;
80 using namespace ::com::sun::star::frame;
81 using namespace ::com::sun::star::util;
82 using namespace ::com::sun::star::inspection;
83 using namespace ::com::sun::star::ucb;
84 using namespace ::comphelper;
87 //= OPropertyBrowserController
90 OPropertyBrowserController::OPropertyBrowserController( const Reference< XComponentContext >& _rxContext )
91 :m_xContext(_rxContext)
92 ,m_aDisposeListeners( m_aMutex )
93 ,m_aControlObservers( m_aMutex )
94 ,m_pView(nullptr)
95 ,m_bContainerFocusListening( false )
96 ,m_bSuspendingPropertyHandlers( false )
97 ,m_bConstructed( false )
98 ,m_bBindingIntrospectee( false )
103 OPropertyBrowserController::~OPropertyBrowserController()
105 // stop listening for property changes
106 acquire();
107 stopInspection( true );
111 IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController, OPropertyBrowserController_Base )
114 Any SAL_CALL OPropertyBrowserController::queryInterface( const Type& _rType )
116 Any aReturn = OPropertyBrowserController_Base::queryInterface( _rType );
117 if ( !aReturn.hasValue() )
118 aReturn = ::cppu::queryInterface(
119 _rType,
120 static_cast< XObjectInspectorUI* >( this )
122 return aReturn;
126 void OPropertyBrowserController::startContainerWindowListening()
128 if (m_bContainerFocusListening)
129 return;
131 if (m_xFrame.is())
133 Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
134 if (xContainerWindow.is())
136 xContainerWindow->addFocusListener(this);
137 m_bContainerFocusListening = true;
141 DBG_ASSERT(m_bContainerFocusListening, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!");
145 void OPropertyBrowserController::stopContainerWindowListening()
147 if (!m_bContainerFocusListening)
148 return;
150 if (m_xFrame.is())
152 Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
153 if (xContainerWindow.is())
155 xContainerWindow->removeFocusListener(this);
156 m_bContainerFocusListening = false;
160 DBG_ASSERT(!m_bContainerFocusListening, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!");
164 Reference< XObjectInspectorModel > SAL_CALL OPropertyBrowserController::getInspectorModel()
166 return m_xModel;
170 void OPropertyBrowserController::impl_initializeView_nothrow()
172 OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
173 if ( !haveView() )
174 return;
176 if ( !m_xModel.is() )
177 // allowed
178 return;
182 getPropertyBox().EnableHelpSection( m_xModel->getHasHelpSection() );
183 getPropertyBox().SetHelpLineLimites( m_xModel->getMinHelpTextLines(), m_xModel->getMaxHelpTextLines() );
185 catch( const Exception& )
187 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
192 bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const
194 if ( !m_xModel.is() )
195 return false;
197 return m_xModel->getIsReadOnly();
201 void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen ) const
205 Reference< XPropertySet > xModelProperties( m_xModel, UNO_QUERY );
206 if ( !xModelProperties.is() )
207 // okay, so the model doesn't want to change its properties
208 // dynamically - fine with us
209 return;
211 void (SAL_CALL XPropertySet::*pListenerOperation)( const OUString&, const Reference< XPropertyChangeListener >& )
212 = _bDoListen ? &XPropertySet::addPropertyChangeListener : &XPropertySet::removePropertyChangeListener;
214 (xModelProperties.get()->*pListenerOperation)(
215 OUString( "IsReadOnly" ),
216 const_cast< OPropertyBrowserController* >( this )
219 catch( const Exception& )
221 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
226 void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference< XObjectInspectorModel >& _rxInspectorModel )
228 impl_startOrStopModelListening_nothrow( false );
229 m_xModel = _rxInspectorModel;
230 impl_startOrStopModelListening_nothrow( true );
232 // initialize the view, if we already have one
233 if ( haveView() )
234 impl_initializeView_nothrow();
236 // inspect again, if we already have inspectees
237 if ( !m_aInspectedObjects.empty() )
238 impl_rebindToInspectee_nothrow( m_aInspectedObjects );
242 void SAL_CALL OPropertyBrowserController::setInspectorModel( const Reference< XObjectInspectorModel >& _inspectorModel )
244 ::osl::MutexGuard aGuard( m_aMutex );
246 if ( m_xModel == _inspectorModel )
247 return;
249 impl_bindToNewModel_nothrow( _inspectorModel );
253 Reference< XObjectInspectorUI > SAL_CALL OPropertyBrowserController::getInspectorUI()
255 // we're derived from this interface, though we do not expose it in queryInterface and getTypes.
256 return this;
260 void SAL_CALL OPropertyBrowserController::inspect( const Sequence< Reference< XInterface > >& _rObjects )
262 SolarMutexGuard aSolarGuard;
263 ::osl::MutexGuard aGuard( m_aMutex );
265 if ( m_bSuspendingPropertyHandlers || !suspendAll_nothrow() )
266 { // we already are trying to suspend the component (this is somewhere up the stack)
267 // OR one of our property handlers raised a veto against closing. Well, we *need* to close
268 // it in order to inspect another object.
269 throw VetoException();
271 if ( m_bBindingIntrospectee )
272 throw VetoException();
274 m_bBindingIntrospectee = true;
275 impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects.begin(), _rObjects.end() ) );
276 m_bBindingIntrospectee = false;
281 Reference< XDispatch > SAL_CALL OPropertyBrowserController::queryDispatch( const URL& /*URL*/, const OUString& /*TargetFrameName*/, ::sal_Int32 /*SearchFlags*/ )
283 // we don't have any dispatches at all, right now
284 return Reference< XDispatch >();
288 Sequence< Reference< XDispatch > > SAL_CALL OPropertyBrowserController::queryDispatches( const Sequence< DispatchDescriptor >& Requests )
290 Sequence< Reference< XDispatch > > aReturn;
291 sal_Int32 nLen = Requests.getLength();
292 aReturn.realloc( nLen );
294 Reference< XDispatch >* pReturn = aReturn.getArray();
295 const Reference< XDispatch >* pReturnEnd = aReturn.getArray() + nLen;
296 const DispatchDescriptor* pDescripts = Requests.getConstArray();
298 for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts )
299 *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags );
301 return aReturn;
305 void SAL_CALL OPropertyBrowserController::initialize( const Sequence< Any >& _arguments )
307 if ( m_bConstructed )
308 throw AlreadyInitializedException();
310 StlSyntaxSequence< Any > arguments( _arguments );
311 if ( arguments.empty() )
312 { // constructor: "createDefault()"
313 m_bConstructed = true;
314 return;
317 Reference< XObjectInspectorModel > xModel;
318 if ( arguments.size() == 1 )
319 { // constructor: "createWithModel( XObjectInspectorModel )"
320 if ( !( arguments[0] >>= xModel ) )
321 throw IllegalArgumentException( OUString(), *this, 0 );
322 createWithModel( xModel );
323 return;
326 throw IllegalArgumentException( OUString(), *this, 0 );
330 void OPropertyBrowserController::createWithModel( const Reference< XObjectInspectorModel >& _rxModel )
332 osl_atomic_increment( &m_refCount );
334 setInspectorModel( _rxModel );
336 osl_atomic_decrement( &m_refCount );
338 m_bConstructed = true;
342 void SAL_CALL OPropertyBrowserController::attachFrame( const Reference< XFrame >& _rxFrame )
344 SolarMutexGuard aSolarGuard;
345 ::osl::MutexGuard aGuard( m_aMutex );
347 if (_rxFrame.is() && haveView())
348 throw RuntimeException("Unable to attach to a second frame.",*this);
350 // revoke as focus listener from the old container window
351 stopContainerWindowListening();
353 m_xFrame = _rxFrame;
354 if (!m_xFrame.is())
355 return;
357 // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame.
358 // Maybe it is intended to only announce the frame to the controller, and the instance doing this
359 // announcement is responsible for calling setComponent, too.
360 Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
361 VCLXWindow* pContainerWindow = comphelper::getUnoTunnelImplementation<VCLXWindow>(xContainerWindow);
362 VclPtr<vcl::Window> pParentWin = pContainerWindow ? pContainerWindow->GetWindow() : VclPtr<vcl::Window>();
363 if (!pParentWin)
364 throw RuntimeException("The frame is invalid. Unable to extract the container window.",*this);
366 Construct( pParentWin );
369 m_xFrame->setComponent( VCLUnoHelper::GetInterface( m_pView ), this );
371 catch( const Exception& )
373 OSL_FAIL( "OPropertyBrowserController::attachFrame: caught an exception!" );
376 startContainerWindowListening();
378 UpdateUI();
382 sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel )
384 Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY );
385 if ( !xModel.is() )
386 return false;
388 setInspectorModel( xModel );
389 return getInspectorModel() == _rxModel;
393 bool OPropertyBrowserController::suspendAll_nothrow()
395 // if there is a handle inside its "onInteractivePropertySelection" method,
396 // then veto
397 // Normally, we could expect every handler to do this itself, but being
398 // realistic, it's safer to handle this here in general.
399 if ( m_xInteractiveHandler.is() )
400 return false;
402 m_bSuspendingPropertyHandlers = true;
403 bool bHandlerVeto = !suspendPropertyHandlers_nothrow( true );
404 m_bSuspendingPropertyHandlers = false;
405 return !bHandlerVeto;
409 bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend )
411 PropertyHandlerArray aAllHandlers; // will contain every handler exactly once
412 for (auto const& propertyHandler : m_aPropertyHandlers)
414 if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) != aAllHandlers.end() )
415 // already visited this particular handler (m_aPropertyHandlers usually contains
416 // the same handler more than once)
417 continue;
418 aAllHandlers.push_back(propertyHandler.second);
421 for (auto const& handler : aAllHandlers)
425 if ( !handler->suspend( _bSuspend ) )
426 if ( _bSuspend )
427 // if we're not suspending, but reactivating, ignore the error
428 return false;
430 catch( const Exception& )
432 OSL_FAIL( "OPropertyBrowserController::suspendPropertyHandlers_nothrow: caught an exception!" );
435 return true;
439 sal_Bool SAL_CALL OPropertyBrowserController::suspend( sal_Bool _bSuspend )
441 ::osl::MutexGuard aGuard( m_aMutex );
442 OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" );
444 if ( !_bSuspend )
445 { // this means a "suspend" is to be "revoked"
446 suspendPropertyHandlers_nothrow( false );
447 // we ourself cannot revoke our suspend
448 return false;
451 if ( !suspendAll_nothrow() )
452 return false;
454 // commit the editor's content
455 if ( haveView() )
456 getPropertyBox().CommitModified();
458 // stop listening
459 stopContainerWindowListening();
461 // outta here
462 return true;
466 Any SAL_CALL OPropertyBrowserController::getViewData( )
468 return makeAny( m_sPageSelection );
472 void SAL_CALL OPropertyBrowserController::restoreViewData( const Any& Data )
474 OUString sPageSelection;
475 if ( ( Data >>= sPageSelection ) && !sPageSelection.isEmpty() )
477 m_sPageSelection = sPageSelection;
478 selectPageFromViewData();
483 Reference< XModel > SAL_CALL OPropertyBrowserController::getModel( )
485 // have no model
486 return Reference< XModel >();
490 Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame( )
492 return m_xFrame;
496 void SAL_CALL OPropertyBrowserController::dispose( )
498 SolarMutexGuard aSolarGuard;
500 // stop inspecting the current object
501 stopInspection( false );
503 // say our dispose listeners goodbye
504 css::lang::EventObject aEvt;
505 aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
506 m_aDisposeListeners.disposeAndClear(aEvt);
507 m_aControlObservers.disposeAndClear(aEvt);
509 // don't delete explicitly (this is done by the frame we reside in)
510 m_pView = nullptr;
512 if ( m_xView.is() )
513 m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
514 m_xView.clear( );
516 m_aInspectedObjects.clear();
517 impl_bindToNewModel_nothrow( nullptr );
521 void SAL_CALL OPropertyBrowserController::addEventListener( const Reference< XEventListener >& _rxListener )
523 m_aDisposeListeners.addInterface(_rxListener);
527 void SAL_CALL OPropertyBrowserController::removeEventListener( const Reference< XEventListener >& _rxListener )
529 m_aDisposeListeners.removeInterface(_rxListener);
533 OUString SAL_CALL OPropertyBrowserController::getImplementationName( )
535 return getImplementationName_static();
538 sal_Bool SAL_CALL OPropertyBrowserController::supportsService( const OUString& ServiceName )
540 return cppu::supportsService(this, ServiceName);
544 Sequence< OUString > SAL_CALL OPropertyBrowserController::getSupportedServiceNames( )
546 return getSupportedServiceNames_static();
550 OUString OPropertyBrowserController::getImplementationName_static( )
552 return "org.openoffice.comp.extensions.ObjectInspector";
556 Sequence< OUString > OPropertyBrowserController::getSupportedServiceNames_static( )
558 Sequence< OUString > aSupported { "com.sun.star.inspection.ObjectInspector" };
559 return aSupported;
563 Reference< XInterface > OPropertyBrowserController::Create(const Reference< XComponentContext >& _rxContext)
565 return *(new OPropertyBrowserController( _rxContext ) );
569 void SAL_CALL OPropertyBrowserController::focusGained( const FocusEvent& _rSource )
571 Reference< XWindow > xSourceWindow(_rSource.Source, UNO_QUERY);
572 Reference< XWindow > xContainerWindow;
573 if (m_xFrame.is())
574 xContainerWindow = m_xFrame->getContainerWindow();
576 if ( xContainerWindow.get() == xSourceWindow.get() )
577 { // our container window got the focus
578 if ( haveView() )
579 getPropertyBox().GrabFocus();
584 void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ )
586 // not interested in
590 void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource )
592 if ( m_xView.is() && ( m_xView == _rSource.Source ) )
594 m_xView = nullptr;
595 m_pView = nullptr;
598 auto it = std::find_if(m_aInspectedObjects.begin(), m_aInspectedObjects.end(),
599 [&_rSource](const InterfaceArray::value_type& rxObj) { return rxObj == _rSource.Source; });
600 if (it != m_aInspectedObjects.end())
601 m_aInspectedObjects.erase(it);
605 IMPL_LINK_NOARG(OPropertyBrowserController, OnPageActivation, LinkParamNone*, void)
607 updateViewDataFromActivePage();
611 void OPropertyBrowserController::updateViewDataFromActivePage()
613 if (!haveView())
614 return;
616 OUString sOldSelection = m_sPageSelection;
617 m_sPageSelection.clear();
619 const sal_uInt16 nCurrentPage = m_pView->getActivaPage();
620 if ( sal_uInt16(-1) != nCurrentPage )
622 for (auto const& pageId : m_aPageIds)
624 if ( nCurrentPage == pageId.second )
626 m_sPageSelection = pageId.first;
627 break;
632 if ( !m_sPageSelection.isEmpty() )
633 m_sLastValidPageSelection = m_sPageSelection;
634 else if ( !sOldSelection.isEmpty() )
635 m_sLastValidPageSelection = sOldSelection;
639 sal_uInt16 OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const
641 sal_uInt16 nPageId = sal_uInt16(-1);
642 HashString2Int16::const_iterator pagePos = m_aPageIds.find( _rCategoryName );
643 if ( pagePos != m_aPageIds.end() )
644 nPageId = pagePos->second;
645 return nPageId;
649 void OPropertyBrowserController::selectPageFromViewData()
651 sal_uInt16 nNewPage = impl_getPageIdForCategory_nothrow( m_sPageSelection );
653 if ( haveView() && ( nNewPage != sal_uInt16(-1) ) )
654 m_pView->activatePage( nNewPage );
656 // just in case ...
657 updateViewDataFromActivePage();
661 void OPropertyBrowserController::Construct(vcl::Window* _pParentWin)
663 DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!");
664 DBG_ASSERT(_pParentWin, "OPropertyBrowserController::Construct: invalid parent window!");
666 m_pView = VclPtr<OPropertyBrowserView>::Create(_pParentWin);
667 m_pView->setPageActivationHandler(LINK(this, OPropertyBrowserController, OnPageActivation));
669 // add as dispose listener for our view. The view is disposed by the frame we're plugged into,
670 // and this disposal _deletes_ the view, so it would be deadly if we use our m_pView member
671 // after that
672 m_xView = VCLUnoHelper::GetInterface(m_pView);
673 if (m_xView.is())
674 m_xView->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
676 getPropertyBox().SetLineListener(this);
677 getPropertyBox().SetControlObserver(this);
678 impl_initializeView_nothrow();
680 m_pView->Show();
684 void SAL_CALL OPropertyBrowserController::propertyChange( const PropertyChangeEvent& _rEvent )
686 if ( _rEvent.Source == m_xModel )
688 if ( _rEvent.PropertyName == "IsReadOnly" )
689 // this is a huge cudgel, admitted.
690 // The problem is that in case we were previously read-only, all our controls
691 // were created read-only, too. We cannot simply switch them to not-read-only.
692 // Even if they had an API for this, we do not know whether they were
693 // originally created read-only, or if they are read-only just because
694 // the model was.
695 impl_rebindToInspectee_nothrow( m_aInspectedObjects );
696 return;
699 if ( m_sCommittingProperty == _rEvent.PropertyName )
700 return;
702 if ( !haveView() )
703 return;
705 Any aNewValue( _rEvent.NewValue );
706 if ( impl_hasPropertyHandlerFor_nothrow( _rEvent.PropertyName ) )
708 // forward the new value to the property box, to reflect the change in the UI
709 aNewValue = impl_getPropertyValue_throw( _rEvent.PropertyName );
711 // check whether the state is ambiguous. This is interesting in case we display the properties
712 // for multiple objects at once: In this case, we'll get a notification from one of the objects,
713 // but need to care for the "composed" value, which can be "ambiguous".
714 PropertyHandlerRef xHandler( impl_getHandlerForProperty_throw( _rEvent.PropertyName ), UNO_SET_THROW );
715 PropertyState ePropertyState( xHandler->getPropertyState( _rEvent.PropertyName ) );
716 bool bAmbiguousValue = ( PropertyState_AMBIGUOUS_VALUE == ePropertyState );
718 getPropertyBox().SetPropertyValue( _rEvent.PropertyName, aNewValue, bAmbiguousValue );
721 // if it's an actuating property, then update the UI for any dependent
722 // properties
723 if ( impl_isActuatingProperty_nothrow( _rEvent.PropertyName ) )
724 impl_broadcastPropertyChange_nothrow( _rEvent.PropertyName, aNewValue, _rEvent.OldValue, false );
728 Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType, sal_Bool CreateReadOnly )
730 ::osl::MutexGuard aGuard( m_aMutex );
732 Reference< XPropertyControl > xControl;
734 // default winbits: a border only
735 WinBits nWinBits = WB_BORDER;
737 // read-only-ness
738 CreateReadOnly |= impl_isReadOnlyModel_throw() ? 1 : 0;
739 if ( CreateReadOnly )
740 nWinBits |= WB_READONLY;
742 switch ( ControlType )
744 case PropertyControlType::StringListField:
745 xControl = new OMultilineEditControl( &getPropertyBox(), eStringList, nWinBits | WB_DROPDOWN | WB_TABSTOP );
746 break;
748 case PropertyControlType::MultiLineTextField:
749 xControl = new OMultilineEditControl( &getPropertyBox(), eMultiLineText, nWinBits | WB_DROPDOWN | WB_TABSTOP );
750 break;
752 case PropertyControlType::ListBox:
753 xControl = new OListboxControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN);
754 break;
756 case PropertyControlType::ComboBox:
757 xControl = new OComboboxControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN);
758 break;
760 case PropertyControlType::TextField:
761 xControl = new OEditControl( &getPropertyBox(), false, nWinBits | WB_TABSTOP );
762 break;
764 case PropertyControlType::CharacterField:
765 xControl = new OEditControl( &getPropertyBox(), true, nWinBits | WB_TABSTOP );
766 break;
768 case PropertyControlType::NumericField:
769 xControl = new ONumericControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_SPIN | WB_REPEAT );
770 break;
772 case PropertyControlType::DateTimeField:
773 xControl = new ODateTimeControl( &getPropertyBox(), nWinBits | WB_TABSTOP );
774 break;
776 case PropertyControlType::DateField:
777 xControl = new ODateControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_SPIN | WB_REPEAT );
778 break;
780 case PropertyControlType::TimeField:
781 xControl = new OTimeControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_SPIN | WB_REPEAT );
782 break;
784 case PropertyControlType::ColorListBox:
785 xControl = new OColorControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN );
786 break;
788 case PropertyControlType::HyperlinkField:
789 xControl = new OHyperlinkControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN );
790 break;
792 default:
793 throw IllegalArgumentException( OUString(), *this, 1 );
796 return xControl;
800 void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn )
802 for (auto const& inspectedObject : m_aInspectedObjects)
806 Reference< XComponent > xComp( inspectedObject, UNO_QUERY );
807 if ( xComp.is() )
809 if ( _bOn )
810 xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
811 else
812 xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
815 catch( const Exception& )
817 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
823 void OPropertyBrowserController::stopInspection( bool _bCommitModified )
825 if ( haveView() )
827 if ( _bCommitModified )
828 // commit the editor's content
829 getPropertyBox().CommitModified();
831 // hide the property box so that it does not flicker
832 getPropertyBox().Hide();
834 // clear the property box
835 getPropertyBox().ClearAll();
838 // destroy the view first
839 if ( haveView() )
841 // remove the pages
842 for (auto const& pageId : m_aPageIds)
843 getPropertyBox().RemovePage( pageId.second );
844 clearContainer( m_aPageIds );
847 clearContainer( m_aProperties );
849 // de-register as dispose-listener from our inspected objects
850 impl_toggleInspecteeListening_nothrow( false );
852 // handlers are obsolete, so is our "composer" for their UI requests
853 if (m_pUIRequestComposer)
854 m_pUIRequestComposer->dispose();
855 m_pUIRequestComposer.reset();
857 // clean up the property handlers
858 PropertyHandlerArray aAllHandlers; // will contain every handler exactly once
859 for (auto const& propertyHandler : m_aPropertyHandlers)
860 if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) == aAllHandlers.end() )
861 aAllHandlers.push_back( propertyHandler.second );
863 for (auto const& handler : aAllHandlers)
867 handler->removePropertyChangeListener( this );
868 handler->dispose();
870 catch( const DisposedException& )
873 catch( const Exception& )
875 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
879 clearContainer( m_aPropertyHandlers );
880 clearContainer( m_aDependencyHandlers );
884 bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const
886 PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
887 return ( handlerPos != m_aPropertyHandlers.end() );
891 OPropertyBrowserController::PropertyHandlerRef const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const
893 PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
894 if ( handlerPos == m_aPropertyHandlers.end() )
895 throw RuntimeException();
896 return handlerPos->second;
900 Any OPropertyBrowserController::impl_getPropertyValue_throw( const OUString& _rPropertyName )
902 PropertyHandlerRef handler = impl_getHandlerForProperty_throw( _rPropertyName );
903 return handler->getPropertyValue( _rPropertyName );
907 void OPropertyBrowserController::impl_rebindToInspectee_nothrow( const InterfaceArray& _rObjects )
911 // stop inspecting the old object(s)
912 stopInspection( true );
914 // inspect the new object(s)
915 m_aInspectedObjects = _rObjects;
916 doInspection();
918 // update the user interface
919 UpdateUI();
922 catch(const Exception&)
924 OSL_FAIL("OPropertyBrowserController::impl_rebindToInspectee_nothrow: caught an exception !");
929 void OPropertyBrowserController::doInspection()
934 // obtain the properties of the object
935 std::vector< Property > aProperties;
937 PropertyHandlerArray aPropertyHandlers;
938 getPropertyHandlers( m_aInspectedObjects, aPropertyHandlers );
940 PropertyHandlerArray::iterator aHandler( aPropertyHandlers.begin() );
941 while ( aHandler != aPropertyHandlers.end() )
943 DBG_ASSERT( aHandler->get(), "OPropertyBrowserController::doInspection: invalid handler!" );
945 StlSyntaxSequence< Property > aThisHandlersProperties( (*aHandler)->getSupportedProperties() );
947 if ( aThisHandlersProperties.empty() )
949 // this handler doesn't know anything about the current inspectee -> ignore it
950 (*aHandler)->dispose();
951 aHandler = aPropertyHandlers.erase( aHandler );
952 continue;
955 // append these properties to our "all properties" array
956 aProperties.reserve( aProperties.size() + aThisHandlersProperties.size() );
957 for (const auto & aThisHandlersPropertie : aThisHandlersProperties)
959 auto noPrevious = std::none_of(
960 aProperties.begin(),
961 aProperties.end(),
962 FindPropertyByName( aThisHandlersPropertie.Name )
964 if ( noPrevious )
966 aProperties.push_back( aThisHandlersPropertie );
967 continue;
970 // there already was another (previous) handler which supported this property.
971 // Don't add it to aProperties, again.
973 // Also, ensure that handlers which previously expressed interest in *changes*
974 // of this property are not notified.
975 // This is 'cause we have a new handler which is responsible for this property,
976 // which means it can give it a completely different meaning than the previous
977 // handler for this property is prepared for.
978 std::pair< PropertyHandlerMultiRepository::iterator, PropertyHandlerMultiRepository::iterator >
979 aDepHandlers = m_aDependencyHandlers.equal_range( aThisHandlersPropertie.Name );
980 m_aDependencyHandlers.erase( aDepHandlers.first, aDepHandlers.second );
983 // determine the superseded properties
984 StlSyntaxSequence< OUString > aSupersededByThisHandler( (*aHandler)->getSupersededProperties() );
985 for (const auto & superseded : aSupersededByThisHandler)
987 std::vector< Property >::iterator existent = std::find_if(
988 aProperties.begin(),
989 aProperties.end(),
990 FindPropertyByName( superseded )
992 if ( existent != aProperties.end() )
993 // one of the properties superseded by this handler was supported by a previous
994 // one -> erase
995 aProperties.erase( existent );
998 // be notified of changes which this handler is responsible for
999 (*aHandler)->addPropertyChangeListener( this );
1001 // remember this handler for every of the properties which it is responsible
1002 // for
1003 for (const auto & aThisHandlersPropertie : aThisHandlersProperties)
1005 m_aPropertyHandlers[ aThisHandlersPropertie.Name ] = *aHandler;
1006 // note that this implies that if two handlers support the same property,
1007 // the latter wins
1010 // see if the handler expresses interest in any actuating properties
1011 StlSyntaxSequence< OUString > aInterestingActuations( (*aHandler)->getActuatingProperties() );
1012 for (const auto & aInterestingActuation : aInterestingActuations)
1014 m_aDependencyHandlers.emplace( aInterestingActuation, *aHandler );
1017 ++aHandler;
1020 // create a new composer for UI requests coming from the handlers
1021 m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) );
1023 // sort the properties by relative position, as indicated by the model
1024 sal_Int32 nPos = 0;
1025 for (auto const& sourceProps : aProperties)
1027 sal_Int32 nRelativePropertyOrder = nPos;
1028 if ( m_xModel.is() )
1029 nRelativePropertyOrder = m_xModel->getPropertyOrderIndex( sourceProps.Name );
1030 m_aProperties.emplace(nRelativePropertyOrder, sourceProps);
1031 ++nPos;
1034 // be notified when one of our inspectees dies
1035 impl_toggleInspecteeListening_nothrow( true );
1037 catch(const Exception&)
1039 OSL_FAIL("OPropertyBrowserController::doInspection : caught an exception !");
1044 css::awt::Size SAL_CALL OPropertyBrowserController::getMinimumSize()
1046 css::awt::Size aSize;
1047 if( m_pView )
1048 return m_pView->getMinimumSize();
1049 else
1050 return aSize;
1054 css::awt::Size SAL_CALL OPropertyBrowserController::getPreferredSize()
1056 return getMinimumSize();
1060 css::awt::Size SAL_CALL OPropertyBrowserController::calcAdjustedSize( const css::awt::Size& _rNewSize )
1062 awt::Size aMinSize = getMinimumSize( );
1063 awt::Size aAdjustedSize( _rNewSize );
1064 if ( aAdjustedSize.Width < aMinSize.Width )
1065 aAdjustedSize.Width = aMinSize.Width;
1066 if ( aAdjustedSize.Height < aMinSize.Height )
1067 aAdjustedSize.Height = aMinSize.Height;
1068 return aAdjustedSize;
1072 void OPropertyBrowserController::describePropertyLine( const Property& _rProperty, OLineDescriptor& _rDescriptor )
1076 PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rProperty.Name );
1077 if ( handler == m_aPropertyHandlers.end() )
1078 throw RuntimeException(); // caught below
1080 _rDescriptor.assignFrom( handler->second->describePropertyLine( _rProperty.Name, this ) );
1083 _rDescriptor.xPropertyHandler = handler->second;
1084 _rDescriptor.sName = _rProperty.Name;
1085 _rDescriptor.aValue = _rDescriptor.xPropertyHandler->getPropertyValue( _rProperty.Name );
1087 if ( _rDescriptor.DisplayName.isEmpty() )
1089 #ifdef DBG_UTIL
1090 SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
1091 <<_rProperty.Name << "'!" );
1092 #endif
1093 _rDescriptor.DisplayName = _rProperty.Name;
1096 PropertyState ePropertyState( _rDescriptor.xPropertyHandler->getPropertyState( _rProperty.Name ) );
1097 if ( PropertyState_AMBIGUOUS_VALUE == ePropertyState )
1099 _rDescriptor.bUnknownValue = true;
1100 _rDescriptor.aValue.clear();
1103 _rDescriptor.bReadOnly = impl_isReadOnlyModel_throw();
1105 catch( const Exception& )
1107 OSL_FAIL( "OPropertyBrowserController::describePropertyLine: caught an exception!" );
1112 void OPropertyBrowserController::impl_buildCategories_throw()
1114 OSL_PRECOND( m_aPageIds.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" );
1116 StlSyntaxSequence< PropertyCategoryDescriptor > aCategories;
1117 if ( m_xModel.is() )
1118 aCategories = StlSyntaxSequence< PropertyCategoryDescriptor >(m_xModel->describeCategories());
1120 for (auto const& category : aCategories)
1122 OSL_ENSURE( m_aPageIds.find( category.ProgrammaticName ) == m_aPageIds.end(),
1123 "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" );
1125 m_aPageIds[ category.ProgrammaticName ] =
1126 getPropertyBox().AppendPage( category.UIName, HelpIdUrl::getHelpId( category.HelpURL ) );
1131 void OPropertyBrowserController::UpdateUI()
1135 if ( !haveView() )
1136 // too early, will return later
1137 return;
1139 getPropertyBox().DisableUpdate();
1141 bool bHaveFocus = getPropertyBox().HasChildPathFocus();
1143 // create our tab pages
1144 impl_buildCategories_throw();
1145 // (and allow for pages to be actually unused)
1146 std::set< sal_uInt16 > aUsedPages;
1148 // when building the UI below, remember which properties are actuating,
1149 // to allow for an initial actuatingPropertyChanged call
1150 std::vector< OUString > aActuatingProperties;
1151 std::vector< Any > aActuatingPropertyValues;
1153 // ask the handlers to describe the property UI, and insert the resulting
1154 // entries into our list boxes
1155 for (auto const& property : m_aProperties)
1157 OLineDescriptor aDescriptor;
1158 describePropertyLine( property.second, aDescriptor );
1160 bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( property.second.Name );
1162 SAL_WARN_IF( aDescriptor.Category.isEmpty(), "extensions.propctrlr",
1163 "OPropertyBrowserController::UpdateUI: empty category provided for property '"
1164 << property.second.Name << "'!");
1165 // finally insert this property control
1166 sal_uInt16 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
1167 if ( nTargetPageId == sal_uInt16(-1) )
1169 // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide
1170 // any category information of its own. In this case, we have a fallback ...
1171 m_aPageIds[ aDescriptor.Category ] =
1172 getPropertyBox().AppendPage( aDescriptor.Category, OString() );
1173 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
1176 getPropertyBox().InsertEntry( aDescriptor, nTargetPageId );
1177 aUsedPages.insert( nTargetPageId );
1179 // if it's an actuating property, remember it
1180 if ( bIsActuatingProperty )
1182 aActuatingProperties.push_back( property.second.Name );
1183 aActuatingPropertyValues.push_back( impl_getPropertyValue_throw( property.second.Name ) );
1187 // update any dependencies for the actuating properties which we encountered
1189 std::vector< Any >::const_iterator aPropertyValue = aActuatingPropertyValues.begin();
1190 for (auto const& actuatingProperty : aActuatingProperties)
1192 impl_broadcastPropertyChange_nothrow( actuatingProperty, *aPropertyValue, *aPropertyValue, true );
1193 ++aPropertyValue;
1197 // remove any unused pages (which we did not encounter properties for)
1198 HashString2Int16 aSurvivingPageIds;
1199 for (auto const& pageId : m_aPageIds)
1201 if ( aUsedPages.find( pageId.second ) == aUsedPages.end() )
1202 getPropertyBox().RemovePage( pageId.second );
1203 else
1204 aSurvivingPageIds.insert(pageId);
1206 m_aPageIds.swap( aSurvivingPageIds );
1209 getPropertyBox().Show();
1210 getPropertyBox().EnableUpdate();
1211 if ( bHaveFocus )
1212 getPropertyBox().GrabFocus();
1214 // activate the first page
1215 if ( !m_aPageIds.empty() )
1217 Sequence< PropertyCategoryDescriptor > aCategories( m_xModel->describeCategories() );
1218 if ( aCategories.hasElements() )
1219 m_pView->activatePage( m_aPageIds[ aCategories[0].ProgrammaticName ] );
1220 else
1221 // allowed: if we default-created the pages ...
1222 m_pView->activatePage( m_aPageIds.begin()->second );
1225 // activate the previously active page (if possible)
1226 if ( !m_sLastValidPageSelection.isEmpty() )
1227 m_sPageSelection = m_sLastValidPageSelection;
1228 selectPageFromViewData();
1230 catch( const Exception& )
1232 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1237 void OPropertyBrowserController::Clicked( const OUString& _rName, bool _bPrimary )
1241 // since the browse buttons do not get the focus when clicked with the mouse,
1242 // we need to commit the changes in the current property field
1243 getPropertyBox().CommitModified();
1245 PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rName );
1246 DBG_ASSERT( handler != m_aPropertyHandlers.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" );
1248 ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
1250 Any aData;
1251 m_xInteractiveHandler = handler->second;
1252 InteractiveSelectionResult eResult =
1253 handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData,
1254 m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) );
1256 switch ( eResult )
1258 case InteractiveSelectionResult_Cancelled:
1259 case InteractiveSelectionResult_Success:
1260 // okay, nothing to do
1261 break;
1262 case InteractiveSelectionResult_ObtainedValue:
1263 handler->second->setPropertyValue( _rName, aData );
1264 break;
1265 case InteractiveSelectionResult_Pending:
1266 // also okay, we expect that the handler has disabled the UI as necessary
1267 break;
1268 default:
1269 OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
1270 break;
1273 catch (const Exception&)
1275 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1277 m_xInteractiveHandler = nullptr;
1281 bool OPropertyBrowserController::hasPropertyByName( const OUString& _rName )
1283 for (auto const& property : m_aProperties)
1284 if ( property.second.Name == _rName )
1285 return true;
1286 return false;
1290 void OPropertyBrowserController::Commit( const OUString& rName, const Any& _rValue )
1294 OUString sPlcHolder = PcrRes(RID_EMBED_IMAGE_PLACEHOLDER);
1295 bool bIsPlaceHolderValue = false;
1297 if ( rName == PROPERTY_IMAGE_URL )
1299 // if the prop value is the PlaceHolder
1300 // can ignore it
1301 OUString sVal;
1302 _rValue >>= sVal;
1303 if ( sVal == sPlcHolder )
1304 bIsPlaceHolderValue = true;
1306 m_sCommittingProperty = rName;
1308 bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName );
1310 Any aOldValue;
1311 if ( bIsActuatingProperty )
1312 aOldValue = impl_getPropertyValue_throw( rName );
1314 // do we have a dedicated handler for this property, which we can delegate some tasks to?
1315 PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
1318 // set the value ( only if it's not a placeholder )
1319 if ( !bIsPlaceHolderValue )
1320 handler->setPropertyValue( rName, _rValue );
1323 // re-retrieve the value
1324 Any aNormalizedValue = handler->getPropertyValue( rName );
1326 // care for any inter-property dependencies
1327 if ( bIsActuatingProperty )
1328 impl_broadcastPropertyChange_nothrow( rName, aNormalizedValue, aOldValue, false );
1330 // and display it again. This ensures proper formatting
1331 getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
1333 catch(const PropertyVetoException& eVetoException)
1335 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_pView ? m_pView->GetFrameWeld() : nullptr,
1336 VclMessageType::Info, VclButtonsType::Ok,
1337 eVetoException.Message));
1338 xInfoBox->run();
1339 PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
1340 Any aNormalizedValue = handler->getPropertyValue( rName );
1341 getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
1343 catch(const Exception&)
1345 OSL_FAIL("OPropertyBrowserController::Commit : caught an exception !");
1348 m_sCommittingProperty.clear();
1352 void OPropertyBrowserController::focusGained( const Reference< XPropertyControl >& Control )
1354 m_aControlObservers.notifyEach( &XPropertyControlObserver::focusGained, Control );
1358 void OPropertyBrowserController::valueChanged( const Reference< XPropertyControl >& Control )
1360 m_aControlObservers.notifyEach( &XPropertyControlObserver::valueChanged, Control );
1364 namespace
1366 Reference< XPropertyHandler > lcl_createHandler( const Reference<XComponentContext>& _rContext, const Any& _rFactoryDescriptor )
1368 Reference< XPropertyHandler > xHandler;
1370 OUString sServiceName;
1371 Reference< XSingleServiceFactory > xServiceFac;
1372 Reference< XSingleComponentFactory > xComponentFac;
1374 if ( _rFactoryDescriptor >>= sServiceName )
1375 xHandler.set( _rContext->getServiceManager()->createInstanceWithContext( sServiceName, _rContext ), UNO_QUERY );
1376 else if ( _rFactoryDescriptor >>= xServiceFac )
1377 xHandler.set(xServiceFac->createInstance(), css::uno::UNO_QUERY);
1378 else if ( _rFactoryDescriptor >>= xComponentFac )
1379 xHandler.set(xComponentFac->createInstanceWithContext( _rContext ), css::uno::UNO_QUERY);
1380 OSL_ENSURE(xHandler.is(),"lcl_createHandler: Can not create handler");
1381 return xHandler;
1386 void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers )
1388 _rHandlers.resize( 0 );
1389 if ( _rObjects.empty() )
1390 return;
1392 // create a component context for the handlers, containing some information about where
1393 // they live
1394 Reference< XComponentContext > xHandlerContext( m_xContext );
1396 // if our own creator did not pass a dialog parent window, use our own view for this
1397 Reference< XWindow > xParentWindow;
1398 Any any = m_xContext->getValueByName( "DialogParentWindow" );
1399 any >>= xParentWindow;
1400 if ( !xParentWindow.is() )
1402 ::cppu::ContextEntry_Init aHandlerContextInfo[] =
1404 ::cppu::ContextEntry_Init( "DialogParentWindow", makeAny( VCLUnoHelper::GetInterface( m_pView ) ) )
1406 xHandlerContext = ::cppu::createComponentContext(
1407 aHandlerContextInfo, SAL_N_ELEMENTS( aHandlerContextInfo ),
1408 m_xContext );
1411 Sequence< Any > aHandlerFactories;
1412 if ( m_xModel.is() )
1413 aHandlerFactories = m_xModel->getHandlerFactories();
1415 for ( auto const & handlerFactory : std::as_const(aHandlerFactories) )
1417 if ( _rObjects.size() == 1 )
1418 { // we're inspecting only one object -> one handler
1419 Reference< XPropertyHandler > xHandler( lcl_createHandler( m_xContext, handlerFactory ) );
1420 if ( xHandler.is() )
1422 xHandler->inspect( _rObjects[0] );
1423 _rHandlers.push_back( xHandler );
1426 else
1428 // create a single handler for every single object
1429 std::vector< Reference< XPropertyHandler > > aSingleHandlers( _rObjects.size() );
1430 std::vector< Reference< XPropertyHandler > >::iterator pHandler = aSingleHandlers.begin();
1432 for (auto const& elem : _rObjects)
1434 *pHandler = lcl_createHandler( m_xContext, handlerFactory );
1435 if ( pHandler->is() )
1437 (*pHandler)->inspect(elem);
1438 ++pHandler;
1441 aSingleHandlers.resize( pHandler - aSingleHandlers.begin() );
1443 // then create a handler which composes information out of those single handlers
1444 if ( !aSingleHandlers.empty() )
1445 _rHandlers.push_back( new PropertyComposer( aSingleHandlers ) );
1449 // note that the handlers will not be used by our caller, if they indicate that there are no
1450 // properties they feel responsible for
1454 bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty )
1456 OrderedPropertyMap::const_iterator search = std::find_if(m_aProperties.begin(), m_aProperties.end(),
1457 [&_rName](const OrderedPropertyMap::value_type& rEntry) { return rEntry.second.Name == _rName; });
1458 if ( _pProperty )
1459 *_pProperty = search;
1460 return ( search != m_aProperties.end() );
1464 void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName )
1466 ::osl::MutexGuard aGuard( m_aMutex );
1467 if ( !haveView() )
1468 throw RuntimeException();
1470 OrderedPropertyMap::const_iterator propertyPos;
1471 if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1472 return;
1474 OLineDescriptor aDescriptor;
1477 describePropertyLine( propertyPos->second, aDescriptor );
1479 catch( const Exception& )
1481 OSL_FAIL( "OPropertyBrowserController::rebuildPropertyUI: caught an exception!" );
1484 getPropertyBox().ChangeEntry( aDescriptor );
1488 void OPropertyBrowserController::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable )
1490 ::osl::MutexGuard aGuard( m_aMutex );
1491 if ( !haveView() )
1492 throw RuntimeException();
1494 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1495 return;
1497 getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable );
1501 void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable )
1503 ::osl::MutexGuard aGuard( m_aMutex );
1504 if ( !haveView() )
1505 throw RuntimeException();
1507 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1508 return;
1510 getPropertyBox().EnablePropertyControls( _rPropertyName, _nElements, _bEnable );
1514 void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName )
1516 ::osl::MutexGuard aGuard( m_aMutex );
1517 if ( !haveView() )
1518 throw RuntimeException();
1520 // look up the property in our object properties
1521 OrderedPropertyMap::const_iterator propertyPos;
1522 if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1523 return;
1525 if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND )
1527 rebuildPropertyUI( _rPropertyName );
1528 return;
1531 OLineDescriptor aDescriptor;
1532 describePropertyLine( propertyPos->second, aDescriptor );
1534 // look for the position to insert the property
1536 // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work
1537 // only on the current page. This implies that it's impossible to use this method here
1538 // to show property lines which are *not* on the current page.
1539 // This is sufficient for now, but should be changed in the future.
1541 // by definition, the properties in m_aProperties are in the order in which they appear in the UI
1542 // So all we need is a predecessor of pProperty in m_aProperties
1543 sal_uInt16 nUIPos = EDITOR_LIST_ENTRY_NOTFOUND;
1546 if ( propertyPos != m_aProperties.begin() )
1547 --propertyPos;
1548 nUIPos = getPropertyBox().GetPropertyPos( propertyPos->second.Name );
1550 while ( ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) && ( propertyPos != m_aProperties.begin() ) );
1552 if ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND )
1553 // insert at the very top
1554 nUIPos = 0;
1555 else
1556 // insert right after the predecessor we found
1557 ++nUIPos;
1559 getPropertyBox().InsertEntry(
1560 aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos );
1564 void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName )
1566 ::osl::MutexGuard aGuard( m_aMutex );
1567 if ( !haveView() )
1568 throw RuntimeException();
1570 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1571 return;
1573 getPropertyBox().RemoveEntry( _rPropertyName );
1577 void OPropertyBrowserController::showCategory( const OUString& _rCategory, sal_Bool _bShow )
1579 ::osl::MutexGuard aGuard( m_aMutex );
1580 if ( !haveView() )
1581 throw RuntimeException();
1583 sal_uInt16 nPageId = impl_getPageIdForCategory_nothrow( _rCategory );
1584 OSL_ENSURE( nPageId != sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" );
1586 getPropertyBox().ShowPropertyPage( nPageId, _bShow );
1590 Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::getPropertyControl( const OUString& _rPropertyName )
1592 ::osl::MutexGuard aGuard( m_aMutex );
1593 if ( !haveView() )
1594 throw RuntimeException();
1596 Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) );
1597 return xControl;
1601 void SAL_CALL OPropertyBrowserController::registerControlObserver( const Reference< XPropertyControlObserver >& Observer )
1603 m_aControlObservers.addInterface( Observer );
1607 void SAL_CALL OPropertyBrowserController::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer )
1609 m_aControlObservers.removeInterface( Observer );
1613 void SAL_CALL OPropertyBrowserController::setHelpSectionText( const OUString& _rHelpText )
1615 SolarMutexGuard aSolarGuard;
1616 ::osl::MutexGuard aGuard( m_aMutex );
1618 if ( !haveView() )
1619 throw DisposedException();
1621 if ( !getPropertyBox().HasHelpSection() )
1622 throw NoSupportException();
1624 getPropertyBox().SetHelpText( _rHelpText );
1628 void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const Any& _rNewValue, const Any& _rOldValue, bool _bFirstTimeInit ) const
1630 // are there one or more handlers which are interested in the actuation?
1631 std::pair< PropertyHandlerMultiRepository::const_iterator, PropertyHandlerMultiRepository::const_iterator > aInterestedHandlers =
1632 m_aDependencyHandlers.equal_range( _rPropertyName );
1633 if ( aInterestedHandlers.first == aInterestedHandlers.second )
1634 // none of our handlers is interested in this
1635 return;
1637 ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
1640 // collect the responses from all interested handlers
1641 PropertyHandlerMultiRepository::const_iterator handler = aInterestedHandlers.first;
1642 while ( handler != aInterestedHandlers.second )
1644 handler->second->actuatingPropertyChanged( _rPropertyName, _rNewValue, _rOldValue,
1645 m_pUIRequestComposer->getUIForPropertyHandler( handler->second ),
1646 _bFirstTimeInit );
1647 ++handler;
1650 catch( const Exception& )
1652 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1657 } // namespace pcr
1660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */