use insert function instead of for loop
[LibreOffice.git] / extensions / source / propctrlr / propcontroller.cxx
blobc8704c702e24d52015ff9720844955b09784de42
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::lang;
59 using namespace ::com::sun::star::frame;
60 using namespace ::com::sun::star::util;
61 using namespace ::com::sun::star::inspection;
62 using namespace ::com::sun::star::ucb;
63 using namespace ::comphelper;
65 //= OPropertyBrowserController
66 OPropertyBrowserController::OPropertyBrowserController( const Reference< XComponentContext >& _rxContext )
67 :m_xContext(_rxContext)
68 ,m_aDisposeListeners( m_aMutex )
69 ,m_aControlObservers( m_aMutex )
70 ,m_bContainerFocusListening( false )
71 ,m_bSuspendingPropertyHandlers( false )
72 ,m_bConstructed( false )
73 ,m_bBindingIntrospectee( false )
77 OPropertyBrowserController::~OPropertyBrowserController()
79 // stop listening for property changes
80 acquire();
81 stopInspection( true );
84 IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController, OPropertyBrowserController_Base )
86 Any SAL_CALL OPropertyBrowserController::queryInterface( const Type& _rType )
88 Any aReturn = OPropertyBrowserController_Base::queryInterface( _rType );
89 if ( !aReturn.hasValue() )
90 aReturn = ::cppu::queryInterface(
91 _rType,
92 static_cast< XObjectInspectorUI* >( this )
94 return aReturn;
98 void OPropertyBrowserController::startContainerWindowListening()
100 if (m_bContainerFocusListening)
101 return;
103 if (m_xFrame.is())
105 Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
106 if (xContainerWindow.is())
108 xContainerWindow->addFocusListener(this);
109 m_bContainerFocusListening = true;
113 DBG_ASSERT(m_bContainerFocusListening, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!");
117 void OPropertyBrowserController::stopContainerWindowListening()
119 if (!m_bContainerFocusListening)
120 return;
122 if (m_xFrame.is())
124 Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
125 if (xContainerWindow.is())
127 xContainerWindow->removeFocusListener(this);
128 m_bContainerFocusListening = false;
132 DBG_ASSERT(!m_bContainerFocusListening, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!");
136 Reference< XObjectInspectorModel > SAL_CALL OPropertyBrowserController::getInspectorModel()
138 return m_xModel;
142 void OPropertyBrowserController::impl_initializeView_nothrow()
144 OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
145 if ( !haveView() )
146 return;
148 if ( !m_xModel.is() )
149 // allowed
150 return;
154 getPropertyBox().EnableHelpSection( m_xModel->getHasHelpSection() );
156 catch( const Exception& )
158 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
163 bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const
165 if ( !m_xModel.is() )
166 return false;
168 return m_xModel->getIsReadOnly();
172 void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen ) const
176 Reference< XPropertySet > xModelProperties( m_xModel, UNO_QUERY );
177 if ( !xModelProperties.is() )
178 // okay, so the model doesn't want to change its properties
179 // dynamically - fine with us
180 return;
182 void (SAL_CALL XPropertySet::*pListenerOperation)( const OUString&, const Reference< XPropertyChangeListener >& )
183 = _bDoListen ? &XPropertySet::addPropertyChangeListener : &XPropertySet::removePropertyChangeListener;
185 (xModelProperties.get()->*pListenerOperation)(
186 u"IsReadOnly"_ustr,
187 const_cast< OPropertyBrowserController* >( this )
190 catch( const Exception& )
192 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
197 void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference< XObjectInspectorModel >& _rxInspectorModel )
199 impl_startOrStopModelListening_nothrow( false );
200 m_xModel = _rxInspectorModel;
201 impl_startOrStopModelListening_nothrow( true );
203 // initialize the view, if we already have one
204 if ( haveView() )
205 impl_initializeView_nothrow();
207 // inspect again, if we already have inspectees
208 if ( !m_aInspectedObjects.empty() )
209 impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
213 void SAL_CALL OPropertyBrowserController::setInspectorModel( const Reference< XObjectInspectorModel >& _inspectorModel )
215 ::osl::MutexGuard aGuard( m_aMutex );
217 if ( m_xModel == _inspectorModel )
218 return;
220 impl_bindToNewModel_nothrow( _inspectorModel );
224 Reference< XObjectInspectorUI > SAL_CALL OPropertyBrowserController::getInspectorUI()
226 // we're derived from this interface, though we do not expose it in queryInterface and getTypes.
227 return this;
231 void SAL_CALL OPropertyBrowserController::inspect( const Sequence< Reference< XInterface > >& _rObjects )
233 SolarMutexGuard aSolarGuard;
234 ::osl::MutexGuard aGuard( m_aMutex );
236 if ( m_bSuspendingPropertyHandlers || !suspendAll_nothrow() )
237 { // we already are trying to suspend the component (this is somewhere up the stack)
238 // OR one of our property handlers raised a veto against closing. Well, we *need* to close
239 // it in order to inspect another object.
240 throw VetoException();
242 if ( m_bBindingIntrospectee )
243 throw VetoException();
245 m_bBindingIntrospectee = true;
246 impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects.begin(), _rObjects.end() ) );
247 m_bBindingIntrospectee = false;
252 Reference< XDispatch > SAL_CALL OPropertyBrowserController::queryDispatch( const URL& /*URL*/, const OUString& /*TargetFrameName*/, ::sal_Int32 /*SearchFlags*/ )
254 // we don't have any dispatches at all, right now
255 return Reference< XDispatch >();
259 Sequence< Reference< XDispatch > > SAL_CALL OPropertyBrowserController::queryDispatches( const Sequence< DispatchDescriptor >& Requests )
261 Sequence< Reference< XDispatch > > aReturn;
262 sal_Int32 nLen = Requests.getLength();
263 aReturn.realloc( nLen );
265 Reference< XDispatch >* pReturn = aReturn.getArray();
266 const Reference< XDispatch >* pReturnEnd = aReturn.getArray() + nLen;
267 const DispatchDescriptor* pDescripts = Requests.getConstArray();
269 for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts )
270 *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags );
272 return aReturn;
276 void SAL_CALL OPropertyBrowserController::initialize( const Sequence< Any >& _arguments )
278 if ( m_bConstructed )
279 throw AlreadyInitializedException();
281 StlSyntaxSequence< Any > arguments( _arguments );
282 if ( arguments.empty() )
283 { // constructor: "createDefault()"
284 m_bConstructed = true;
285 return;
288 Reference< XObjectInspectorModel > xModel;
289 if ( arguments.size() == 1 )
290 { // constructor: "createWithModel( XObjectInspectorModel )"
291 if ( !( arguments[0] >>= xModel ) )
292 throw IllegalArgumentException( OUString(), *this, 0 );
293 createWithModel( xModel );
294 return;
297 throw IllegalArgumentException( OUString(), *this, 0 );
301 void OPropertyBrowserController::createWithModel( const Reference< XObjectInspectorModel >& _rxModel )
303 osl_atomic_increment( &m_refCount );
305 setInspectorModel( _rxModel );
307 osl_atomic_decrement( &m_refCount );
309 m_bConstructed = true;
313 void SAL_CALL OPropertyBrowserController::attachFrame( const Reference< XFrame >& _rxFrame )
315 SolarMutexGuard aSolarGuard;
316 ::osl::MutexGuard aGuard( m_aMutex );
318 if (_rxFrame.is() && haveView())
319 throw RuntimeException(u"Unable to attach to a second frame."_ustr,*this);
321 // revoke as focus listener from the old container window
322 stopContainerWindowListening();
324 m_xPropView.reset();
325 m_xBuilder.reset();
327 m_xFrame = _rxFrame;
328 if (!m_xFrame.is())
329 return;
331 // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame.
332 // Maybe it is intended to only announce the frame to the controller, and the instance doing this
333 // announcement is responsible for calling setComponent, too.
334 Reference<XWindow> xContainerWindow = m_xFrame->getContainerWindow();
336 OUString sUIFile(u"modules/spropctrlr/ui/formproperties.ui"_ustr);
337 std::unique_ptr<weld::Builder> xBuilder;
339 if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xContainerWindow.get()))
341 xBuilder = Application::CreateBuilder(pTunnel->getWidget(), sUIFile);
343 else
345 VclPtr<vcl::Window> pParentWin = VCLUnoHelper::GetWindow(xContainerWindow);
346 if (!pParentWin)
347 throw RuntimeException(u"The frame is invalid. Unable to extract the container window."_ustr,*this);
348 xBuilder = Application::CreateInterimBuilder(pParentWin, sUIFile, true);
351 Construct(xContainerWindow, std::move(xBuilder));
353 startContainerWindowListening();
355 UpdateUI();
358 sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel )
360 Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY );
361 if ( !xModel.is() )
362 return false;
364 setInspectorModel( xModel );
365 return getInspectorModel() == _rxModel;
369 bool OPropertyBrowserController::suspendAll_nothrow()
371 // if there is a handle inside its "onInteractivePropertySelection" method,
372 // then veto
373 // Normally, we could expect every handler to do this itself, but being
374 // realistic, it's safer to handle this here in general.
375 if ( m_xInteractiveHandler.is() )
376 return false;
378 m_bSuspendingPropertyHandlers = true;
379 bool bHandlerVeto = !suspendPropertyHandlers_nothrow( true );
380 m_bSuspendingPropertyHandlers = false;
381 return !bHandlerVeto;
385 bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend )
387 PropertyHandlerArray aAllHandlers; // will contain every handler exactly once
388 for (auto const& propertyHandler : m_aPropertyHandlers)
390 if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) != aAllHandlers.end() )
391 // already visited this particular handler (m_aPropertyHandlers usually contains
392 // the same handler more than once)
393 continue;
394 aAllHandlers.push_back(propertyHandler.second);
397 for (auto const& handler : aAllHandlers)
401 if ( !handler->suspend( _bSuspend ) )
402 if ( _bSuspend )
403 // if we're not suspending, but reactivating, ignore the error
404 return false;
406 catch( const Exception& )
408 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::suspendPropertyHandlers_nothrow" );
411 return true;
415 sal_Bool SAL_CALL OPropertyBrowserController::suspend( sal_Bool _bSuspend )
417 ::osl::MutexGuard aGuard( m_aMutex );
418 OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" );
420 if ( !_bSuspend )
421 { // this means a "suspend" is to be "revoked"
422 suspendPropertyHandlers_nothrow( false );
423 // we ourself cannot revoke our suspend
424 return false;
427 if ( !suspendAll_nothrow() )
428 return false;
430 // commit the editor's content
431 if ( haveView() )
432 getPropertyBox().CommitModified();
434 // stop listening
435 stopContainerWindowListening();
437 // outta here
438 return true;
442 Any SAL_CALL OPropertyBrowserController::getViewData( )
444 return Any( m_sPageSelection );
448 void SAL_CALL OPropertyBrowserController::restoreViewData( const Any& Data )
450 OUString sPageSelection;
451 if ( ( Data >>= sPageSelection ) && !sPageSelection.isEmpty() )
453 m_sPageSelection = sPageSelection;
454 selectPageFromViewData();
458 Reference< XModel > SAL_CALL OPropertyBrowserController::getModel( )
460 // have no model
461 return Reference< XModel >();
464 Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame( )
466 return m_xFrame;
469 void SAL_CALL OPropertyBrowserController::dispose()
471 SolarMutexGuard aSolarGuard;
473 // stop inspecting the current object
474 stopInspection( false );
476 // say our dispose listeners goodbye
477 css::lang::EventObject aEvt;
478 aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
479 m_aDisposeListeners.disposeAndClear(aEvt);
480 m_aControlObservers.disposeAndClear(aEvt);
482 m_xPropView.reset();
483 m_xBuilder.reset();
485 if ( m_xView.is() )
486 m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
487 m_xView.clear( );
489 m_aInspectedObjects.clear();
490 impl_bindToNewModel_nothrow( nullptr );
491 m_xModel.clear();
492 m_xInteractiveHandler.clear();
493 m_xFrame.clear();
496 void SAL_CALL OPropertyBrowserController::addEventListener( const Reference< XEventListener >& _rxListener )
498 m_aDisposeListeners.addInterface(_rxListener);
501 void SAL_CALL OPropertyBrowserController::removeEventListener( const Reference< XEventListener >& _rxListener )
503 m_aDisposeListeners.removeInterface(_rxListener);
506 OUString SAL_CALL OPropertyBrowserController::getImplementationName( )
508 return u"org.openoffice.comp.extensions.ObjectInspector"_ustr;
511 sal_Bool SAL_CALL OPropertyBrowserController::supportsService( const OUString& ServiceName )
513 return cppu::supportsService(this, ServiceName);
517 Sequence< OUString > SAL_CALL OPropertyBrowserController::getSupportedServiceNames( )
519 return { u"com.sun.star.inspection.ObjectInspector"_ustr };
523 void SAL_CALL OPropertyBrowserController::focusGained( const FocusEvent& _rSource )
525 Reference< XWindow > xSourceWindow(_rSource.Source, UNO_QUERY);
526 Reference< XWindow > xContainerWindow;
527 if (m_xFrame.is())
528 xContainerWindow = m_xFrame->getContainerWindow();
530 if ( xContainerWindow.get() == xSourceWindow.get() )
531 { // our container window got the focus
532 if ( haveView() )
533 getPropertyBox().GrabFocus();
538 void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ )
540 // not interested in
544 void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource )
546 if ( m_xView.is() && ( m_xView == _rSource.Source ) )
548 m_xView = nullptr;
549 m_xPropView.reset();
550 m_xBuilder.reset();
553 auto it = std::find_if(m_aInspectedObjects.begin(), m_aInspectedObjects.end(),
554 [&_rSource](const InterfaceArray::value_type& rxObj) { return rxObj == _rSource.Source; });
555 if (it != m_aInspectedObjects.end())
556 m_aInspectedObjects.erase(it);
560 IMPL_LINK_NOARG(OPropertyBrowserController, OnPageActivation, LinkParamNone*, void)
562 updateViewDataFromActivePage();
566 void OPropertyBrowserController::updateViewDataFromActivePage()
568 if (!haveView())
569 return;
571 OUString sOldSelection = m_sPageSelection;
572 m_sPageSelection.clear();
574 const sal_uInt16 nCurrentPage = m_xPropView->getActivePage();
575 if ( sal_uInt16(-1) != nCurrentPage )
577 for (auto const& pageId : m_aPageIds)
579 if ( nCurrentPage == pageId.second )
581 m_sPageSelection = pageId.first;
582 break;
587 if ( !m_sPageSelection.isEmpty() )
588 m_sLastValidPageSelection = m_sPageSelection;
589 else if ( !sOldSelection.isEmpty() )
590 m_sLastValidPageSelection = sOldSelection;
594 sal_uInt16 OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const
596 sal_uInt16 nPageId = sal_uInt16(-1);
597 HashString2Int16::const_iterator pagePos = m_aPageIds.find( _rCategoryName );
598 if ( pagePos != m_aPageIds.end() )
599 nPageId = pagePos->second;
600 return nPageId;
603 void OPropertyBrowserController::selectPageFromViewData()
605 sal_uInt16 nNewPage = impl_getPageIdForCategory_nothrow( m_sPageSelection );
607 if ( haveView() && ( nNewPage != sal_uInt16(-1) ) )
608 m_xPropView->activatePage( nNewPage );
610 // just in case ...
611 updateViewDataFromActivePage();
614 void OPropertyBrowserController::Construct(const Reference<XWindow>& rContainerWindow, std::unique_ptr<weld::Builder> xBuilder)
616 DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!");
617 assert(xBuilder && "OPropertyBrowserController::Construct: invalid parent window!");
619 m_xBuilder = std::move(xBuilder);
621 m_xPropView.reset(new OPropertyBrowserView(m_xContext, *m_xBuilder));
622 m_xPropView->setPageActivationHandler(LINK(this, OPropertyBrowserController, OnPageActivation));
624 // add as dispose listener for our view. The view is disposed by the frame we're plugged into,
625 // and this disposal _deletes_ the view, so it would be deadly if we use our m_xPropView member
626 // after that
627 m_xView = rContainerWindow;
628 if (m_xView.is())
629 m_xView->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
631 getPropertyBox().SetLineListener(this);
632 getPropertyBox().SetControlObserver(this);
633 impl_initializeView_nothrow();
636 void SAL_CALL OPropertyBrowserController::propertyChange( const PropertyChangeEvent& _rEvent )
638 if ( _rEvent.Source == m_xModel )
640 if ( _rEvent.PropertyName == "IsReadOnly" )
641 // this is a huge cudgel, admitted.
642 // The problem is that in case we were previously read-only, all our controls
643 // were created read-only, too. We cannot simply switch them to not-read-only.
644 // Even if they had an API for this, we do not know whether they were
645 // originally created read-only, or if they are read-only just because
646 // the model was.
647 impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
648 return;
651 if ( m_sCommittingProperty == _rEvent.PropertyName )
652 return;
654 if ( !haveView() )
655 return;
657 Any aNewValue( _rEvent.NewValue );
658 if ( impl_hasPropertyHandlerFor_nothrow( _rEvent.PropertyName ) )
660 // forward the new value to the property box, to reflect the change in the UI
661 aNewValue = impl_getPropertyValue_throw( _rEvent.PropertyName );
663 // check whether the state is ambiguous. This is interesting in case we display the properties
664 // for multiple objects at once: In this case, we'll get a notification from one of the objects,
665 // but need to care for the "composed" value, which can be "ambiguous".
666 PropertyHandlerRef xHandler( impl_getHandlerForProperty_throw( _rEvent.PropertyName ), UNO_SET_THROW );
667 PropertyState ePropertyState( xHandler->getPropertyState( _rEvent.PropertyName ) );
668 bool bAmbiguousValue = ( PropertyState_AMBIGUOUS_VALUE == ePropertyState );
670 getPropertyBox().SetPropertyValue( _rEvent.PropertyName, aNewValue, bAmbiguousValue );
673 // if it's an actuating property, then update the UI for any dependent
674 // properties
675 if ( impl_isActuatingProperty_nothrow( _rEvent.PropertyName ) )
676 impl_broadcastPropertyChange_nothrow( _rEvent.PropertyName, aNewValue, _rEvent.OldValue, false );
679 Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType, sal_Bool bCreateReadOnly )
681 ::osl::MutexGuard aGuard( m_aMutex );
683 Reference< XPropertyControl > xControl;
685 // read-only-ness
686 bCreateReadOnly |= impl_isReadOnlyModel_throw() ? 1 : 0;
688 switch ( ControlType )
690 case PropertyControlType::MultiLineTextField:
691 case PropertyControlType::StringListField:
693 bool bMultiLineTextField = ControlType == PropertyControlType::MultiLineTextField;
694 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/multiline.ui"_ustr, m_xContext));
695 auto pContainer = xBuilder->weld_container(u"multiline"_ustr);
696 rtl::Reference<OMultilineEditControl> pControl = new OMultilineEditControl(std::move(pContainer), std::move(xBuilder),
697 bMultiLineTextField ? eMultiLineText : eStringList, bCreateReadOnly);
698 pControl->SetModifyHandler();
699 xControl = pControl;
700 break;
703 case PropertyControlType::ListBox:
705 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/listbox.ui"_ustr, m_xContext));
706 auto pComboBox = xBuilder->weld_combo_box(u"listbox"_ustr);
707 rtl::Reference<OListboxControl> pControl = new OListboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly);
708 pControl->SetModifyHandler();
709 xControl = pControl;
710 break;
713 case PropertyControlType::ComboBox:
715 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/combobox.ui"_ustr, m_xContext));
716 auto pComboBox = xBuilder->weld_combo_box(u"combobox"_ustr);
717 rtl::Reference<OComboboxControl> pControl = new OComboboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly);
718 pControl->SetModifyHandler();
719 xControl = pControl;
720 break;
723 case PropertyControlType::TextField:
724 case PropertyControlType::CharacterField:
726 bool bCharacterField = ControlType == PropertyControlType::CharacterField;
727 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/textfield.ui"_ustr, m_xContext));
728 auto pEntry = xBuilder->weld_entry(u"textfield"_ustr);
729 rtl::Reference<OEditControl> pControl = new OEditControl(std::move(pEntry), std::move(xBuilder), bCharacterField, bCreateReadOnly);
730 pControl->SetModifyHandler();
731 xControl = pControl;
732 break;
735 case PropertyControlType::NumericField:
737 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/numericfield.ui"_ustr, m_xContext));
738 auto pSpinButton = xBuilder->weld_metric_spin_button(u"numericfield"_ustr, FieldUnit::NONE);
739 rtl::Reference<ONumericControl> pControl = new ONumericControl(std::move(pSpinButton), std::move(xBuilder), bCreateReadOnly);
740 pControl->SetModifyHandler();
741 xControl = pControl;
742 break;
745 case PropertyControlType::DateTimeField:
747 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/datetimefield.ui"_ustr, m_xContext));
748 auto pContainer = xBuilder->weld_container(u"datetimefield"_ustr);
749 rtl::Reference<ODateTimeControl> pControl = new ODateTimeControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
750 pControl->SetModifyHandler();
751 xControl = pControl;
752 break;
755 case PropertyControlType::DateField:
757 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/datefield.ui"_ustr, m_xContext));
758 auto pContainer = xBuilder->weld_container(u"datefield"_ustr);
759 rtl::Reference<ODateControl> pControl = new ODateControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
760 pControl->SetModifyHandler();
761 xControl = pControl;
762 break;
765 case PropertyControlType::TimeField:
767 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/timefield.ui"_ustr, m_xContext));
768 auto pTimeSpinButton = xBuilder->weld_formatted_spin_button(u"timefield"_ustr);
769 rtl::Reference<OTimeControl> pControl = new OTimeControl(std::move(pTimeSpinButton), std::move(xBuilder), bCreateReadOnly);
770 pControl->SetModifyHandler();
771 xControl = pControl;
772 break;
775 case PropertyControlType::ColorListBox:
777 auto lambda = [this]{ return PropertyHandlerHelper::getDialogParentFrame(m_xContext); };
778 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/colorlistbox.ui"_ustr, m_xContext));
779 auto pMenuButton = xBuilder->weld_menu_button(u"colorlistbox"_ustr);
780 rtl::Reference<OColorControl> pControl = new OColorControl(std::make_unique<ColorListBox>(std::move(pMenuButton), lambda), std::move(xBuilder), bCreateReadOnly);
781 pControl->SetModifyHandler();
782 xControl = pControl;
783 break;
786 case PropertyControlType::HyperlinkField:
788 std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/hyperlinkfield.ui"_ustr, m_xContext));
789 auto pContainer = xBuilder->weld_container(u"hyperlinkfield"_ustr);
790 rtl::Reference<OHyperlinkControl> pControl = new OHyperlinkControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
791 pControl->SetModifyHandler();
792 xControl = pControl;
793 break;
796 default:
797 throw IllegalArgumentException( OUString(), *this, 1 );
800 return xControl;
804 void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn )
806 for (auto const& inspectedObject : m_aInspectedObjects)
810 Reference< XComponent > xComp( inspectedObject, UNO_QUERY );
811 if ( xComp.is() )
813 if ( _bOn )
814 xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
815 else
816 xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
819 catch( const Exception& )
821 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
827 void OPropertyBrowserController::stopInspection( bool _bCommitModified )
829 if ( haveView() )
831 if ( _bCommitModified )
832 // commit the editor's content
833 getPropertyBox().CommitModified();
835 // hide the property box so that it does not flicker
836 getPropertyBox().Hide();
838 // clear the property box
839 getPropertyBox().ClearAll();
842 // destroy the view first
843 if ( haveView() )
845 // remove the pages
846 for (auto const& pageId : m_aPageIds)
847 getPropertyBox().RemovePage( pageId.second );
848 clearContainer( m_aPageIds );
851 clearContainer( m_aProperties );
853 // de-register as dispose-listener from our inspected objects
854 impl_toggleInspecteeListening_nothrow( false );
856 // handlers are obsolete, so is our "composer" for their UI requests
857 if (m_pUIRequestComposer)
858 m_pUIRequestComposer->dispose();
859 m_pUIRequestComposer.reset();
861 // clean up the property handlers
862 PropertyHandlerArray aAllHandlers; // will contain every handler exactly once
863 for (auto const& propertyHandler : m_aPropertyHandlers)
864 if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) == aAllHandlers.end() )
865 aAllHandlers.push_back( propertyHandler.second );
867 for (auto const& handler : aAllHandlers)
871 handler->removePropertyChangeListener( this );
872 handler->dispose();
874 catch( const DisposedException& )
877 catch( const Exception& )
879 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
883 clearContainer( m_aPropertyHandlers );
884 clearContainer( m_aDependencyHandlers );
888 bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const
890 PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
891 return ( handlerPos != m_aPropertyHandlers.end() );
895 OPropertyBrowserController::PropertyHandlerRef const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const
897 PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
898 if ( handlerPos == m_aPropertyHandlers.end() )
899 throw RuntimeException();
900 return handlerPos->second;
904 Any OPropertyBrowserController::impl_getPropertyValue_throw( const OUString& _rPropertyName )
906 PropertyHandlerRef handler = impl_getHandlerForProperty_throw( _rPropertyName );
907 return handler->getPropertyValue( _rPropertyName );
911 void OPropertyBrowserController::impl_rebindToInspectee_nothrow( InterfaceArray&& _rObjects )
915 // stop inspecting the old object(s)
916 stopInspection( true );
918 // inspect the new object(s)
919 m_aInspectedObjects = std::move(_rObjects);
920 doInspection();
922 // update the user interface
923 UpdateUI();
926 catch(const Exception&)
928 TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
933 void OPropertyBrowserController::doInspection()
938 // obtain the properties of the object
939 std::vector< Property > aProperties;
941 PropertyHandlerArray aPropertyHandlers;
942 getPropertyHandlers( m_aInspectedObjects, aPropertyHandlers );
944 PropertyHandlerArray::iterator aHandler( aPropertyHandlers.begin() );
945 while ( aHandler != aPropertyHandlers.end() )
947 DBG_ASSERT( aHandler->get(), "OPropertyBrowserController::doInspection: invalid handler!" );
949 StlSyntaxSequence< Property > aThisHandlersProperties( (*aHandler)->getSupportedProperties() );
951 if ( aThisHandlersProperties.empty() )
953 // this handler doesn't know anything about the current inspectee -> ignore it
954 (*aHandler)->dispose();
955 aHandler = aPropertyHandlers.erase( aHandler );
956 continue;
959 // append these properties to our "all properties" array
960 aProperties.reserve( std::max<size_t>(aProperties.size() + aThisHandlersProperties.size(), aProperties.size() * 2) );
961 for (const auto & aThisHandlersProperty : aThisHandlersProperties)
963 auto noPrevious = std::none_of(
964 aProperties.begin(),
965 aProperties.end(),
966 FindPropertyByName( aThisHandlersProperty.Name )
968 if ( noPrevious )
970 aProperties.push_back( aThisHandlersProperty );
971 continue;
974 // there already was another (previous) handler which supported this property.
975 // Don't add it to aProperties, again.
977 // Also, ensure that handlers which previously expressed interest in *changes*
978 // of this property are not notified.
979 // This is 'cause we have a new handler which is responsible for this property,
980 // which means it can give it a completely different meaning than the previous
981 // handler for this property is prepared for.
982 std::pair< PropertyHandlerMultiRepository::iterator, PropertyHandlerMultiRepository::iterator >
983 aDepHandlers = m_aDependencyHandlers.equal_range( aThisHandlersProperty.Name );
984 m_aDependencyHandlers.erase( aDepHandlers.first, aDepHandlers.second );
987 // determine the superseded properties
988 StlSyntaxSequence< OUString > aSupersededByThisHandler( (*aHandler)->getSupersededProperties() );
989 for (const auto & superseded : aSupersededByThisHandler)
991 std::vector< Property >::iterator existent = std::find_if(
992 aProperties.begin(),
993 aProperties.end(),
994 FindPropertyByName( superseded )
996 if ( existent != aProperties.end() )
997 // one of the properties superseded by this handler was supported by a previous
998 // one -> erase
999 aProperties.erase( existent );
1002 // be notified of changes which this handler is responsible for
1003 (*aHandler)->addPropertyChangeListener( this );
1005 // remember this handler for every of the properties which it is responsible
1006 // for
1007 for (const auto & aThisHandlersProperty : aThisHandlersProperties)
1009 m_aPropertyHandlers[ aThisHandlersProperty.Name ] = *aHandler;
1010 // note that this implies that if two handlers support the same property,
1011 // the latter wins
1014 // see if the handler expresses interest in any actuating properties
1015 StlSyntaxSequence< OUString > aInterestingActuations( (*aHandler)->getActuatingProperties() );
1016 for (const auto & aInterestingActuation : aInterestingActuations)
1018 m_aDependencyHandlers.emplace( aInterestingActuation, *aHandler );
1021 ++aHandler;
1024 // create a new composer for UI requests coming from the handlers
1025 m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) );
1027 // sort the properties by relative position, as indicated by the model
1028 sal_Int32 nPos = 0;
1029 for (auto const& sourceProps : aProperties)
1031 sal_Int32 nRelativePropertyOrder = nPos;
1032 if ( m_xModel.is() )
1033 nRelativePropertyOrder = m_xModel->getPropertyOrderIndex( sourceProps.Name );
1034 m_aProperties.emplace(nRelativePropertyOrder, sourceProps);
1035 ++nPos;
1038 // be notified when one of our inspectees dies
1039 impl_toggleInspecteeListening_nothrow( true );
1041 catch(const Exception&)
1043 TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
1048 css::awt::Size SAL_CALL OPropertyBrowserController::getMinimumSize()
1050 css::awt::Size aSize;
1051 if( m_xPropView )
1052 return m_xPropView->getMinimumSize();
1053 else
1054 return aSize;
1058 css::awt::Size SAL_CALL OPropertyBrowserController::getPreferredSize()
1060 return getMinimumSize();
1064 css::awt::Size SAL_CALL OPropertyBrowserController::calcAdjustedSize( const css::awt::Size& _rNewSize )
1066 awt::Size aMinSize = getMinimumSize( );
1067 awt::Size aAdjustedSize( _rNewSize );
1068 if ( aAdjustedSize.Width < aMinSize.Width )
1069 aAdjustedSize.Width = aMinSize.Width;
1070 if ( aAdjustedSize.Height < aMinSize.Height )
1071 aAdjustedSize.Height = aMinSize.Height;
1072 return aAdjustedSize;
1076 void OPropertyBrowserController::describePropertyLine( const Property& _rProperty, OLineDescriptor& _rDescriptor )
1080 PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rProperty.Name );
1081 if ( handler == m_aPropertyHandlers.end() )
1082 throw RuntimeException(); // caught below
1084 _rDescriptor.assignFrom( handler->second->describePropertyLine( _rProperty.Name, this ) );
1087 _rDescriptor.xPropertyHandler = handler->second;
1088 _rDescriptor.sName = _rProperty.Name;
1089 _rDescriptor.aValue = _rDescriptor.xPropertyHandler->getPropertyValue( _rProperty.Name );
1091 if ( _rDescriptor.DisplayName.isEmpty() )
1093 #ifdef DBG_UTIL
1094 SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
1095 <<_rProperty.Name << "'!" );
1096 #endif
1097 _rDescriptor.DisplayName = _rProperty.Name;
1100 PropertyState ePropertyState( _rDescriptor.xPropertyHandler->getPropertyState( _rProperty.Name ) );
1101 if ( PropertyState_AMBIGUOUS_VALUE == ePropertyState )
1103 _rDescriptor.bUnknownValue = true;
1104 _rDescriptor.aValue.clear();
1107 _rDescriptor.bReadOnly = impl_isReadOnlyModel_throw();
1109 // for ui-testing try and distinguish different instances of the controls
1110 auto xWindow = _rDescriptor.Control->getControlWindow();
1111 if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xWindow.get()))
1113 weld::Widget* m_pControlWindow = pTunnel->getWidget();
1114 if (m_pControlWindow)
1115 m_pControlWindow->set_buildable_name(m_pControlWindow->get_buildable_name() + "-" + _rDescriptor.DisplayName);
1119 catch( const Exception& )
1121 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine" );
1126 void OPropertyBrowserController::impl_buildCategories_throw()
1128 OSL_PRECOND( m_aPageIds.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" );
1130 StlSyntaxSequence< PropertyCategoryDescriptor > aCategories;
1131 if ( m_xModel.is() )
1132 aCategories = StlSyntaxSequence< PropertyCategoryDescriptor >(m_xModel->describeCategories());
1134 for (auto const& category : aCategories)
1136 OSL_ENSURE( m_aPageIds.find( category.ProgrammaticName ) == m_aPageIds.end(),
1137 "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" );
1139 m_aPageIds[ category.ProgrammaticName ] =
1140 getPropertyBox().AppendPage( category.UIName, HelpIdUrl::getHelpId( category.HelpURL ) );
1145 void OPropertyBrowserController::UpdateUI()
1149 if ( !haveView() )
1150 // too early, will return later
1151 return;
1153 // create our tab pages
1154 impl_buildCategories_throw();
1155 // (and allow for pages to be actually unused)
1156 std::set< sal_uInt16 > aUsedPages;
1158 // when building the UI below, remember which properties are actuating,
1159 // to allow for an initial actuatingPropertyChanged call
1160 std::vector< OUString > aActuatingProperties;
1161 std::vector< Any > aActuatingPropertyValues;
1163 // ask the handlers to describe the property UI, and insert the resulting
1164 // entries into our list boxes
1165 for (auto const& property : m_aProperties)
1167 OLineDescriptor aDescriptor;
1168 describePropertyLine( property.second, aDescriptor );
1170 bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( property.second.Name );
1172 SAL_WARN_IF( aDescriptor.Category.isEmpty(), "extensions.propctrlr",
1173 "OPropertyBrowserController::UpdateUI: empty category provided for property '"
1174 << property.second.Name << "'!");
1175 // finally insert this property control
1176 sal_uInt16 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
1177 if ( nTargetPageId == sal_uInt16(-1) )
1179 // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide
1180 // any category information of its own. In this case, we have a fallback ...
1181 m_aPageIds[ aDescriptor.Category ] =
1182 getPropertyBox().AppendPage(aDescriptor.Category, {});
1183 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
1186 getPropertyBox().InsertEntry( aDescriptor, nTargetPageId );
1187 aUsedPages.insert( nTargetPageId );
1189 // if it's an actuating property, remember it
1190 if ( bIsActuatingProperty )
1192 aActuatingProperties.push_back( property.second.Name );
1193 aActuatingPropertyValues.push_back( impl_getPropertyValue_throw( property.second.Name ) );
1197 // update any dependencies for the actuating properties which we encountered
1199 std::vector< Any >::const_iterator aPropertyValue = aActuatingPropertyValues.begin();
1200 for (auto const& actuatingProperty : aActuatingProperties)
1202 impl_broadcastPropertyChange_nothrow( actuatingProperty, *aPropertyValue, *aPropertyValue, true );
1203 ++aPropertyValue;
1207 // remove any unused pages (which we did not encounter properties for)
1208 HashString2Int16 aSurvivingPageIds;
1209 for (auto const& pageId : m_aPageIds)
1211 if ( aUsedPages.find( pageId.second ) == aUsedPages.end() )
1212 getPropertyBox().RemovePage( pageId.second );
1213 else
1214 aSurvivingPageIds.insert(pageId);
1216 m_aPageIds.swap( aSurvivingPageIds );
1218 getPropertyBox().Show();
1220 // activate the first page
1221 if ( !m_aPageIds.empty() )
1223 Sequence< PropertyCategoryDescriptor > aCategories( m_xModel->describeCategories() );
1224 if ( aCategories.hasElements() )
1225 m_xPropView->activatePage( m_aPageIds[ aCategories[0].ProgrammaticName ] );
1226 else
1227 // allowed: if we default-created the pages ...
1228 m_xPropView->activatePage( m_aPageIds.begin()->second );
1231 // activate the previously active page (if possible)
1232 if ( !m_sLastValidPageSelection.isEmpty() )
1233 m_sPageSelection = m_sLastValidPageSelection;
1234 selectPageFromViewData();
1236 catch( const Exception& )
1238 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1243 void OPropertyBrowserController::Clicked( const OUString& _rName, bool _bPrimary )
1247 // since the browse buttons do not get the focus when clicked with the mouse,
1248 // we need to commit the changes in the current property field
1249 getPropertyBox().CommitModified();
1251 PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rName );
1252 DBG_ASSERT( handler != m_aPropertyHandlers.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" );
1254 ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
1256 Any aData;
1257 m_xInteractiveHandler = handler->second;
1258 InteractiveSelectionResult eResult =
1259 handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData,
1260 m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) );
1262 switch ( eResult )
1264 case InteractiveSelectionResult_Cancelled:
1265 case InteractiveSelectionResult_Success:
1266 // okay, nothing to do
1267 break;
1268 case InteractiveSelectionResult_ObtainedValue:
1269 handler->second->setPropertyValue( _rName, aData );
1270 break;
1271 case InteractiveSelectionResult_Pending:
1272 // also okay, we expect that the handler has disabled the UI as necessary
1273 break;
1274 default:
1275 OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
1276 break;
1279 catch (const Exception&)
1281 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1283 m_xInteractiveHandler = nullptr;
1287 bool OPropertyBrowserController::hasPropertyByName( const OUString& _rName )
1289 for (auto const& property : m_aProperties)
1290 if ( property.second.Name == _rName )
1291 return true;
1292 return false;
1296 void OPropertyBrowserController::Commit( const OUString& rName, const Any& _rValue )
1300 OUString sPlcHolder = PcrRes(RID_EMBED_IMAGE_PLACEHOLDER);
1301 bool bIsPlaceHolderValue = false;
1303 if ( rName == PROPERTY_IMAGE_URL )
1305 // if the prop value is the PlaceHolder
1306 // can ignore it
1307 OUString sVal;
1308 _rValue >>= sVal;
1309 if ( sVal == sPlcHolder )
1310 bIsPlaceHolderValue = true;
1312 m_sCommittingProperty = rName;
1314 bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName );
1316 Any aOldValue;
1317 if ( bIsActuatingProperty )
1318 aOldValue = impl_getPropertyValue_throw( rName );
1320 // do we have a dedicated handler for this property, which we can delegate some tasks to?
1321 PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
1324 // set the value ( only if it's not a placeholder )
1325 if ( !bIsPlaceHolderValue )
1326 handler->setPropertyValue( rName, _rValue );
1329 // re-retrieve the value
1330 Any aNormalizedValue = handler->getPropertyValue( rName );
1332 // care for any inter-property dependencies
1333 if ( bIsActuatingProperty )
1334 impl_broadcastPropertyChange_nothrow( rName, aNormalizedValue, aOldValue, false );
1336 // and display it again. This ensures proper formatting
1337 getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
1339 catch(const PropertyVetoException& eVetoException)
1341 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xPropView->getPropertyBox().getWidget(),
1342 VclMessageType::Info, VclButtonsType::Ok,
1343 eVetoException.Message));
1344 xInfoBox->run();
1345 PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
1346 Any aNormalizedValue = handler->getPropertyValue( rName );
1347 getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
1349 catch(const Exception&)
1351 TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
1354 m_sCommittingProperty.clear();
1358 void OPropertyBrowserController::focusGained( const Reference< XPropertyControl >& Control )
1360 m_aControlObservers.notifyEach( &XPropertyControlObserver::focusGained, Control );
1364 void OPropertyBrowserController::valueChanged( const Reference< XPropertyControl >& Control )
1366 m_aControlObservers.notifyEach( &XPropertyControlObserver::valueChanged, Control );
1370 namespace
1372 Reference< XPropertyHandler > lcl_createHandler( const Reference<XComponentContext>& _rContext, const Any& _rFactoryDescriptor )
1374 Reference< XPropertyHandler > xHandler;
1376 OUString sServiceName;
1377 Reference< XSingleServiceFactory > xServiceFac;
1378 Reference< XSingleComponentFactory > xComponentFac;
1380 if ( _rFactoryDescriptor >>= sServiceName )
1381 xHandler.set( _rContext->getServiceManager()->createInstanceWithContext( sServiceName, _rContext ), UNO_QUERY );
1382 else if ( _rFactoryDescriptor >>= xServiceFac )
1383 xHandler.set(xServiceFac->createInstance(), css::uno::UNO_QUERY);
1384 else if ( _rFactoryDescriptor >>= xComponentFac )
1385 xHandler.set(xComponentFac->createInstanceWithContext( _rContext ), css::uno::UNO_QUERY);
1386 OSL_ENSURE(xHandler.is(),"lcl_createHandler: Can not create handler");
1387 return xHandler;
1392 void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers )
1394 _rHandlers.resize( 0 );
1395 if ( _rObjects.empty() )
1396 return;
1398 Sequence< Any > aHandlerFactories;
1399 if ( m_xModel.is() )
1400 aHandlerFactories = m_xModel->getHandlerFactories();
1402 for (auto const& handlerFactory : aHandlerFactories)
1404 if ( _rObjects.size() == 1 )
1405 { // we're inspecting only one object -> one handler
1406 Reference< XPropertyHandler > xHandler( lcl_createHandler( m_xContext, handlerFactory ) );
1407 if ( xHandler.is() )
1409 xHandler->inspect( _rObjects[0] );
1410 _rHandlers.push_back( xHandler );
1413 else
1415 // create a single handler for every single object
1416 std::vector< Reference< XPropertyHandler > > aSingleHandlers( _rObjects.size() );
1417 std::vector< Reference< XPropertyHandler > >::iterator pHandler = aSingleHandlers.begin();
1419 for (auto const& elem : _rObjects)
1421 *pHandler = lcl_createHandler( m_xContext, handlerFactory );
1422 if ( pHandler->is() )
1424 (*pHandler)->inspect(elem);
1425 ++pHandler;
1428 aSingleHandlers.resize( pHandler - aSingleHandlers.begin() );
1430 // then create a handler which composes information out of those single handlers
1431 if ( !aSingleHandlers.empty() )
1432 _rHandlers.push_back( new PropertyComposer( std::move(aSingleHandlers) ) );
1436 // note that the handlers will not be used by our caller, if they indicate that there are no
1437 // properties they feel responsible for
1441 bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty )
1443 OrderedPropertyMap::const_iterator search = std::find_if(m_aProperties.begin(), m_aProperties.end(),
1444 [&_rName](const OrderedPropertyMap::value_type& rEntry) { return rEntry.second.Name == _rName; });
1445 if ( _pProperty )
1446 *_pProperty = search;
1447 return ( search != m_aProperties.end() );
1451 void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName )
1453 ::osl::MutexGuard aGuard( m_aMutex );
1454 if ( !haveView() )
1455 throw RuntimeException();
1457 OrderedPropertyMap::const_iterator propertyPos;
1458 if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1459 return;
1461 OLineDescriptor aDescriptor;
1464 describePropertyLine( propertyPos->second, aDescriptor );
1466 catch( const Exception& )
1468 TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::rebuildPropertyUI" );
1471 getPropertyBox().ChangeEntry( aDescriptor );
1475 void OPropertyBrowserController::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable )
1477 ::osl::MutexGuard aGuard( m_aMutex );
1478 if ( !haveView() )
1479 throw RuntimeException();
1481 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1482 return;
1484 getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable );
1488 void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, 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().EnablePropertyControls( _rPropertyName, _nElements, _bEnable );
1501 void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName )
1503 ::osl::MutexGuard aGuard( m_aMutex );
1504 if ( !haveView() )
1505 throw RuntimeException();
1507 // look up the property in our object properties
1508 OrderedPropertyMap::const_iterator propertyPos;
1509 if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1510 return;
1512 if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND )
1514 rebuildPropertyUI( _rPropertyName );
1515 return;
1518 OLineDescriptor aDescriptor;
1519 describePropertyLine( propertyPos->second, aDescriptor );
1521 // look for the position to insert the property
1523 // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work
1524 // only on the current page. This implies that it's impossible to use this method here
1525 // to show property lines which are *not* on the current page.
1526 // This is sufficient for now, but should be changed in the future.
1528 // by definition, the properties in m_aProperties are in the order in which they appear in the UI
1529 // So all we need is a predecessor of pProperty in m_aProperties
1530 sal_uInt16 nUIPos = EDITOR_LIST_ENTRY_NOTFOUND;
1533 if ( propertyPos != m_aProperties.begin() )
1534 --propertyPos;
1535 nUIPos = getPropertyBox().GetPropertyPos( propertyPos->second.Name );
1537 while ( ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) && ( propertyPos != m_aProperties.begin() ) );
1539 if ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND )
1540 // insert at the very top
1541 nUIPos = 0;
1542 else
1543 // insert right after the predecessor we found
1544 ++nUIPos;
1546 getPropertyBox().InsertEntry(
1547 aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos );
1551 void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName )
1553 ::osl::MutexGuard aGuard( m_aMutex );
1554 if ( !haveView() )
1555 throw RuntimeException();
1557 if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1558 return;
1560 getPropertyBox().RemoveEntry( _rPropertyName );
1564 void OPropertyBrowserController::showCategory( const OUString& rCategory, sal_Bool bShow )
1566 ::osl::MutexGuard aGuard( m_aMutex );
1567 if ( !haveView() )
1568 throw RuntimeException();
1570 sal_uInt16 nPageId = impl_getPageIdForCategory_nothrow( rCategory );
1571 OSL_ENSURE( nPageId != sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" );
1573 getPropertyBox().ShowPropertyPage( nPageId, bShow );
1577 Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::getPropertyControl( const OUString& _rPropertyName )
1579 ::osl::MutexGuard aGuard( m_aMutex );
1580 if ( !haveView() )
1581 throw RuntimeException();
1583 Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) );
1584 return xControl;
1588 void SAL_CALL OPropertyBrowserController::registerControlObserver( const Reference< XPropertyControlObserver >& Observer )
1590 m_aControlObservers.addInterface( Observer );
1594 void SAL_CALL OPropertyBrowserController::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer )
1596 m_aControlObservers.removeInterface( Observer );
1600 void SAL_CALL OPropertyBrowserController::setHelpSectionText( const OUString& _rHelpText )
1602 SolarMutexGuard aSolarGuard;
1603 ::osl::MutexGuard aGuard( m_aMutex );
1605 if ( !haveView() )
1606 throw DisposedException();
1608 if ( !getPropertyBox().HasHelpSection() )
1609 throw NoSupportException();
1611 getPropertyBox().SetHelpText( _rHelpText );
1615 void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const Any& _rNewValue, const Any& _rOldValue, bool _bFirstTimeInit ) const
1617 // are there one or more handlers which are interested in the actuation?
1618 std::pair< PropertyHandlerMultiRepository::const_iterator, PropertyHandlerMultiRepository::const_iterator > aInterestedHandlers =
1619 m_aDependencyHandlers.equal_range( _rPropertyName );
1620 if ( aInterestedHandlers.first == aInterestedHandlers.second )
1621 // none of our handlers is interested in this
1622 return;
1624 ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
1627 // collect the responses from all interested handlers
1628 PropertyHandlerMultiRepository::const_iterator handler = aInterestedHandlers.first;
1629 while ( handler != aInterestedHandlers.second )
1631 handler->second->actuatingPropertyChanged( _rPropertyName, _rNewValue, _rOldValue,
1632 m_pUIRequestComposer->getUIForPropertyHandler( handler->second ),
1633 _bFirstTimeInit );
1634 ++handler;
1637 catch( const Exception& )
1639 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1644 } // namespace pcr
1646 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
1647 extensions_propctrlr_OPropertyBrowserController_get_implementation(
1648 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
1650 return cppu::acquire(new pcr::OPropertyBrowserController(context));
1653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */