Avoid potential negative array index access to cached text.
[LibreOffice.git] / extensions / source / propctrlr / propcontroller.cxx
blob836f5844e38d576067789023850ca71afaae1aab
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 "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>
49 #include <algorithm>
50 #include <sal/log.hxx>
52 namespace pcr
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
82 acquire();
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(
93 _rType,
94 static_cast< XObjectInspectorUI* >( this )
96 return aReturn;
100 void OPropertyBrowserController::startContainerWindowListening()
102 if (m_bContainerFocusListening)
103 return;
105 if (m_xFrame.is())
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)
122 return;
124 if (m_xFrame.is())
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()
140 return m_xModel;
144 void OPropertyBrowserController::impl_initializeView_nothrow()
146 OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
147 if ( !haveView() )
148 return;
150 if ( !m_xModel.is() )
151 // allowed
152 return;
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() )
168 return false;
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
182 return;
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
206 if ( haveView() )
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 )
220 return;
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.
229 return this;
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 );
274 return aReturn;
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;
287 return;
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 );
296 return;
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();
326 m_xPropView.reset();
327 m_xBuilder.reset();
329 m_xFrame = _rxFrame;
330 if (!m_xFrame.is())
331 return;
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);
345 else
347 VclPtr<vcl::Window> pParentWin = VCLUnoHelper::GetWindow(xContainerWindow);
348 if (!pParentWin)
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();
357 UpdateUI();
360 sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel )
362 Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY );
363 if ( !xModel.is() )
364 return false;
366 setInspectorModel( xModel );
367 return getInspectorModel() == _rxModel;
371 bool OPropertyBrowserController::suspendAll_nothrow()
373 // if there is a handle inside its "onInteractivePropertySelection" method,
374 // then veto
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() )
378 return false;
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)
395 continue;
396 aAllHandlers.push_back(propertyHandler.second);
399 for (auto const& handler : aAllHandlers)
403 if ( !handler->suspend( _bSuspend ) )
404 if ( _bSuspend )
405 // if we're not suspending, but reactivating, ignore the error
406 return false;
408 catch( const Exception& )
410 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::suspendPropertyHandlers_nothrow" );
413 return true;
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!" );
422 if ( !_bSuspend )
423 { // this means a "suspend" is to be "revoked"
424 suspendPropertyHandlers_nothrow( false );
425 // we ourself cannot revoke our suspend
426 return false;
429 if ( !suspendAll_nothrow() )
430 return false;
432 // commit the editor's content
433 if ( haveView() )
434 getPropertyBox().CommitModified();
436 // stop listening
437 stopContainerWindowListening();
439 // outta here
440 return true;
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( )
462 // have no model
463 return Reference< XModel >();
466 Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame( )
468 return m_xFrame;
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);
484 m_xPropView.reset();
485 m_xBuilder.reset();
487 if ( m_xView.is() )
488 m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
489 m_xView.clear( );
491 m_aInspectedObjects.clear();
492 impl_bindToNewModel_nothrow( nullptr );
493 m_xModel.clear();
494 m_xInteractiveHandler.clear();
495 m_xFrame.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;
529 if (m_xFrame.is())
530 xContainerWindow = m_xFrame->getContainerWindow();
532 if ( xContainerWindow.get() == xSourceWindow.get() )
533 { // our container window got the focus
534 if ( haveView() )
535 getPropertyBox().GrabFocus();
540 void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ )
542 // not interested in
546 void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource )
548 if ( m_xView.is() && ( m_xView == _rSource.Source ) )
550 m_xView = nullptr;
551 m_xPropView.reset();
552 m_xBuilder.reset();
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()
570 if (!haveView())
571 return;
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;
584 break;
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;
602 return nPageId;
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 );
612 // just in case ...
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
628 // after that
629 m_xView = rContainerWindow;
630 if (m_xView.is())
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
648 // the model was.
649 impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
650 return;
653 if ( m_sCommittingProperty == _rEvent.PropertyName )
654 return;
656 if ( !haveView() )
657 return;
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
676 // properties
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;
687 // read-only-ness
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();
701 xControl = pControl;
702 break;
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();
711 xControl = pControl;
712 break;
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();
721 xControl = pControl;
722 break;
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();
733 xControl = pControl;
734 break;
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();
743 xControl = pControl;
744 break;
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();
753 xControl = pControl;
754 break;
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();
763 xControl = pControl;
764 break;
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();
773 xControl = pControl;
774 break;
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();
784 xControl = pControl;
785 break;
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();
794 xControl = pControl;
795 break;
798 default:
799 throw IllegalArgumentException( OUString(), *this, 1 );
802 return xControl;
806 void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn )
808 for (auto const& inspectedObject : m_aInspectedObjects)
812 Reference< XComponent > xComp( inspectedObject, UNO_QUERY );
813 if ( xComp.is() )
815 if ( _bOn )
816 xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
817 else
818 xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
821 catch( const Exception& )
823 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
829 void OPropertyBrowserController::stopInspection( bool _bCommitModified )
831 if ( haveView() )
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
845 if ( haveView() )
847 // remove the pages
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 );
874 handler->dispose();
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);
922 doInspection();
924 // update the user interface
925 UpdateUI();
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 );
958 continue;
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(
966 aProperties.begin(),
967 aProperties.end(),
968 FindPropertyByName( aThisHandlersProperty.Name )
970 if ( noPrevious )
972 aProperties.push_back( aThisHandlersProperty );
973 continue;
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(
994 aProperties.begin(),
995 aProperties.end(),
996 FindPropertyByName( superseded )
998 if ( existent != aProperties.end() )
999 // one of the properties superseded by this handler was supported by a previous
1000 // one -> erase
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
1008 // for
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,
1013 // the latter wins
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 );
1023 ++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
1030 sal_Int32 nPos = 0;
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);
1037 ++nPos;
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;
1053 if( m_xPropView )
1054 return m_xPropView->getMinimumSize();
1055 else
1056 return aSize;
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() )
1095 #ifdef DBG_UTIL
1096 SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
1097 <<_rProperty.Name << "'!" );
1098 #endif
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()
1151 if ( !haveView() )
1152 // too early, will return later
1153 return;
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 );
1205 ++aPropertyValue;
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 );
1215 else
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 ] );
1228 else
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 );
1258 Any aData;
1259 m_xInteractiveHandler = handler->second;
1260 InteractiveSelectionResult eResult =
1261 handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData,
1262 m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) );
1264 switch ( eResult )
1266 case InteractiveSelectionResult_Cancelled:
1267 case InteractiveSelectionResult_Success:
1268 // okay, nothing to do
1269 break;
1270 case InteractiveSelectionResult_ObtainedValue:
1271 handler->second->setPropertyValue( _rName, aData );
1272 break;
1273 case InteractiveSelectionResult_Pending:
1274 // also okay, we expect that the handler has disabled the UI as necessary
1275 break;
1276 default:
1277 OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
1278 break;
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 )
1293 return true;
1294 return false;
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
1308 // can ignore it
1309 OUString sVal;
1310 _rValue >>= sVal;
1311 if ( sVal == sPlcHolder )
1312 bIsPlaceHolderValue = true;
1314 m_sCommittingProperty = rName;
1316 bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName );
1318 Any aOldValue;
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));
1346 xInfoBox->run();
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 );
1372 namespace
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");
1389 return xHandler;
1394 void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers )
1396 _rHandlers.resize( 0 );
1397 if ( _rObjects.empty() )
1398 return;
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 );
1415 else
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);
1427 ++pHandler;
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; });
1447 if ( _pProperty )
1448 *_pProperty = search;
1449 return ( search != m_aProperties.end() );
1453 void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName )
1455 ::osl::MutexGuard aGuard( m_aMutex );
1456 if ( !haveView() )
1457 throw RuntimeException();
1459 OrderedPropertyMap::const_iterator propertyPos;
1460 if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1461 return;
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 );
1480 if ( !haveView() )
1481 throw RuntimeException();
1483 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1484 return;
1486 getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable );
1490 void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable )
1492 ::osl::MutexGuard aGuard( m_aMutex );
1493 if ( !haveView() )
1494 throw RuntimeException();
1496 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1497 return;
1499 getPropertyBox().EnablePropertyControls( _rPropertyName, _nElements, _bEnable );
1503 void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName )
1505 ::osl::MutexGuard aGuard( m_aMutex );
1506 if ( !haveView() )
1507 throw RuntimeException();
1509 // look up the property in our object properties
1510 OrderedPropertyMap::const_iterator propertyPos;
1511 if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1512 return;
1514 if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND )
1516 rebuildPropertyUI( _rPropertyName );
1517 return;
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() )
1536 --propertyPos;
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
1543 nUIPos = 0;
1544 else
1545 // insert right after the predecessor we found
1546 ++nUIPos;
1548 getPropertyBox().InsertEntry(
1549 aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos );
1553 void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName )
1555 ::osl::MutexGuard aGuard( m_aMutex );
1556 if ( !haveView() )
1557 throw RuntimeException();
1559 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1560 return;
1562 getPropertyBox().RemoveEntry( _rPropertyName );
1566 void OPropertyBrowserController::showCategory( const OUString& rCategory, sal_Bool bShow )
1568 ::osl::MutexGuard aGuard( m_aMutex );
1569 if ( !haveView() )
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 );
1582 if ( !haveView() )
1583 throw RuntimeException();
1585 Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) );
1586 return xControl;
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 );
1607 if ( !haveView() )
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
1624 return;
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 ),
1635 _bFirstTimeInit );
1636 ++handler;
1639 catch( const Exception& )
1641 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1646 } // namespace pcr
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: */