/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include "propcontroller.hxx"
#include "handlerhelper.hxx"
#include "standardcontrol.hxx"
#include "linedescriptor.hxx"
#include <strings.hrc>
#include "propertyeditor.hxx"
#include "modulepcr.hxx"
#include "formstrings.hxx"
#include "formbrowsertools.hxx"
#include "propertycomposer.hxx"
 
#include <com/sun/star/awt/XWindow.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/NoSupportException.hpp>
#include <com/sun/star/inspection/PropertyControlType.hpp>
#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/util/VetoException.hpp>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/weldutils.hxx>
#include <osl/mutex.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <cppuhelper/supportsservice.hxx>
 
#include <algorithm>
#include <sal/log.hxx>
 
namespace pcr
{
    using namespace ::com::sun::star;
    using namespace ::com::sun::star::uno;
    using namespace ::com::sun::star::awt;
    using namespace ::com::sun::star::beans;
    using namespace ::com::sun::star::lang;
    using namespace ::com::sun::star::frame;
    using namespace ::com::sun::star::util;
    using namespace ::com::sun::star::inspection;
    using namespace ::com::sun::star::ucb;
    using namespace ::comphelper;
 
    //= OPropertyBrowserController
    OPropertyBrowserController::OPropertyBrowserController( const Reference< XComponentContext >& _rxContext )
            :m_xContext(_rxContext)
            ,m_aDisposeListeners( m_aMutex )
            ,m_aControlObservers( m_aMutex )
            ,m_bContainerFocusListening( false )
            ,m_bSuspendingPropertyHandlers( false )
            ,m_bConstructed( false )
            ,m_bBindingIntrospectee( false )
    {
    }
 
    OPropertyBrowserController::~OPropertyBrowserController()
    {
        // stop listening for property changes
        acquire();
        stopInspection( true );
    }
 
    IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController, OPropertyBrowserController_Base )
 
    Any SAL_CALL OPropertyBrowserController::queryInterface( const Type& _rType )
    {
        Any aReturn = OPropertyBrowserController_Base::queryInterface( _rType );
        if ( !aReturn.hasValue() )
            aReturn = ::cppu::queryInterface(
                _rType,
                static_cast< XObjectInspectorUI* >( this )
            );
        return aReturn;
    }
 
 
    void OPropertyBrowserController::startContainerWindowListening()
    {
        if (m_bContainerFocusListening)
            return;
 
        if (m_xFrame.is())
        {
            Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
            if (xContainerWindow.is())
            {
                xContainerWindow->addFocusListener(this);
                m_bContainerFocusListening = true;
            }
        }
 
        DBG_ASSERT(m_bContainerFocusListening, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!");
    }
 
 
    void OPropertyBrowserController::stopContainerWindowListening()
    {
        if (!m_bContainerFocusListening)
            return;
 
        if (m_xFrame.is())
        {
            Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
            if (xContainerWindow.is())
            {
                xContainerWindow->removeFocusListener(this);
                m_bContainerFocusListening = false;
            }
        }
 
        DBG_ASSERT(!m_bContainerFocusListening, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!");
    }
 
 
    Reference< XObjectInspectorModel > SAL_CALL OPropertyBrowserController::getInspectorModel()
    {
        return m_xModel;
    }
 
 
    void OPropertyBrowserController::impl_initializeView_nothrow()
    {
        OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
        if ( !haveView() )
            return;
 
        if ( !m_xModel.is() )
            // allowed
            return;
 
        try
        {
            getPropertyBox().EnableHelpSection( m_xModel->getHasHelpSection() );
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }
 
 
    bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const
    {
        if ( !m_xModel.is() )
            return false;
 
        return m_xModel->getIsReadOnly();
    }
 
 
    void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen ) const
    {
        try
        {
            Reference< XPropertySet > xModelProperties( m_xModel, UNO_QUERY );
            if ( !xModelProperties.is() )
                // okay, so the model doesn't want to change its properties
                // dynamically - fine with us
                return;
 
            void (SAL_CALL XPropertySet::*pListenerOperation)( const OUString&, const Reference< XPropertyChangeListener >& )
                = _bDoListen ? &XPropertySet::addPropertyChangeListener : &XPropertySet::removePropertyChangeListener;
 
            (xModelProperties.get()->*pListenerOperation)(
                u"IsReadOnly"_ustr,
                const_cast< OPropertyBrowserController* >( this )
            );
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }
 
 
    void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference< XObjectInspectorModel >& _rxInspectorModel )
    {
        impl_startOrStopModelListening_nothrow( false );
        m_xModel = _rxInspectorModel;
        impl_startOrStopModelListening_nothrow( true );
 
        // initialize the view, if we already have one
        if ( haveView() )
            impl_initializeView_nothrow();
 
        // inspect again, if we already have inspectees
        if ( !m_aInspectedObjects.empty() )
            impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
    }
 
 
    void SAL_CALL OPropertyBrowserController::setInspectorModel( const Reference< XObjectInspectorModel >& _inspectorModel )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
 
        if ( m_xModel == _inspectorModel )
            return;
 
        impl_bindToNewModel_nothrow( _inspectorModel );
    }
 
 
    Reference< XObjectInspectorUI > SAL_CALL OPropertyBrowserController::getInspectorUI()
    {
        // we're derived from this interface, though we do not expose it in queryInterface and getTypes.
        return this;
    }
 
 
    void SAL_CALL OPropertyBrowserController::inspect( const Sequence< Reference< XInterface > >& _rObjects )
    {
        SolarMutexGuard aSolarGuard;
        ::osl::MutexGuard aGuard( m_aMutex );
 
        if ( m_bSuspendingPropertyHandlers || !suspendAll_nothrow() )
        {   // we already are trying to suspend the component (this is somewhere up the stack)
            // OR one of our property handlers raised a veto against closing. Well, we *need* to close
            // it in order to inspect another object.
            throw VetoException();
        }
        if ( m_bBindingIntrospectee )
            throw VetoException();
 
        m_bBindingIntrospectee = true;
        impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects.begin(), _rObjects.end() ) );
        m_bBindingIntrospectee = false;
 
    }
 
 
    Reference< XDispatch > SAL_CALL OPropertyBrowserController::queryDispatch( const URL& /*URL*/, const OUString& /*TargetFrameName*/, ::sal_Int32 /*SearchFlags*/ )
    {
        // we don't have any dispatches at all, right now
        return Reference< XDispatch >();
    }
 
 
    Sequence< Reference< XDispatch > > SAL_CALL OPropertyBrowserController::queryDispatches( const Sequence< DispatchDescriptor >& Requests )
    {
        Sequence< Reference< XDispatch > > aReturn;
        sal_Int32 nLen = Requests.getLength();
        aReturn.realloc( nLen );
 
        Reference< XDispatch >* pReturn     = aReturn.getArray();
        const   Reference< XDispatch >* pReturnEnd  = aReturn.getArray() + nLen;
        const   DispatchDescriptor*     pDescripts  = Requests.getConstArray();
 
        for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts )
            *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags );
 
        return aReturn;
    }
 
 
    void SAL_CALL OPropertyBrowserController::initialize( const Sequence< Any >& _arguments )
    {
        if ( m_bConstructed )
            throw AlreadyInitializedException();
 
        StlSyntaxSequence< Any > arguments( _arguments );
        if ( arguments.empty() )
        {   // constructor: "createDefault()"
            m_bConstructed = true;
            return;
        }
 
        Reference< XObjectInspectorModel > xModel;
        if ( arguments.size() == 1 )
        {   // constructor: "createWithModel( XObjectInspectorModel )"
            if ( !( arguments[0] >>= xModel ) )
                throw IllegalArgumentException( OUString(), *this, 0 );
            createWithModel( xModel );
            return;
        }
 
        throw IllegalArgumentException( OUString(), *this, 0 );
    }
 
 
    void OPropertyBrowserController::createWithModel( const Reference< XObjectInspectorModel >& _rxModel )
    {
        osl_atomic_increment( &m_refCount );
        {
            setInspectorModel( _rxModel );
        }
        osl_atomic_decrement( &m_refCount );
 
        m_bConstructed = true;
    }
 
 
    void SAL_CALL OPropertyBrowserController::attachFrame( const Reference< XFrame >& _rxFrame )
    {
        SolarMutexGuard aSolarGuard;
        ::osl::MutexGuard aGuard( m_aMutex );
 
        if (_rxFrame.is() && haveView())
            throw RuntimeException(u"Unable to attach to a second frame."_ustr,*this);
 
        // revoke as focus listener from the old container window
        stopContainerWindowListening();
 
        m_xPropView.reset();
        m_xBuilder.reset();
 
        m_xFrame = _rxFrame;
        if (!m_xFrame.is())
            return;
 
        // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame.
        // Maybe it is intended to only announce the frame to the controller, and the instance doing this
        // announcement is responsible for calling setComponent, too.
        Reference<XWindow> xContainerWindow = m_xFrame->getContainerWindow();
 
        OUString sUIFile(u"modules/spropctrlr/ui/formproperties.ui"_ustr);
        std::unique_ptr<weld::Builder> xBuilder;
 
        if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xContainerWindow.get()))
        {
            xBuilder = Application::CreateBuilder(pTunnel->getWidget(), sUIFile);
        }
        else
        {
            VclPtr<vcl::Window> pParentWin = VCLUnoHelper::GetWindow(xContainerWindow);
            if (!pParentWin)
                throw RuntimeException(u"The frame is invalid. Unable to extract the container window."_ustr,*this);
            xBuilder = Application::CreateInterimBuilder(pParentWin, sUIFile, true);
        }
 
        Construct(xContainerWindow, std::move(xBuilder));
 
        startContainerWindowListening();
 
        UpdateUI();
    }
 
    sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel )
    {
        Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY );
        if ( !xModel.is() )
            return false;
 
        setInspectorModel( xModel );
        return getInspectorModel() == _rxModel;
    }
 
 
    bool OPropertyBrowserController::suspendAll_nothrow()
    {
        // if there is a handle inside its "onInteractivePropertySelection" method,
        // then veto
        // Normally, we could expect every handler to do this itself, but being
        // realistic, it's safer to handle this here in general.
        if ( m_xInteractiveHandler.is() )
            return false;
 
        m_bSuspendingPropertyHandlers = true;
        bool bHandlerVeto = !suspendPropertyHandlers_nothrow( true );
        m_bSuspendingPropertyHandlers = false;
        return !bHandlerVeto;
    }
 
 
    bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend )
    {
        PropertyHandlerArray aAllHandlers;  // will contain every handler exactly once
        for (auto const& propertyHandler : m_aPropertyHandlers)
        {
            if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) != aAllHandlers.end() )
                // already visited this particular handler (m_aPropertyHandlers usually contains
                // the same handler more than once)
                continue;
            aAllHandlers.push_back(propertyHandler.second);
        }
 
        for (auto const& handler : aAllHandlers)
        {
            try
            {
                if ( !handler->suspend( _bSuspend ) )
                    if ( _bSuspend )
                        // if we're not suspending, but reactivating, ignore the error
                        return false;
            }
            catch( const Exception& )
            {
                TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::suspendPropertyHandlers_nothrow" );
            }
        }
        return true;
    }
 
 
    sal_Bool SAL_CALL OPropertyBrowserController::suspend( sal_Bool _bSuspend )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" );
 
        if ( !_bSuspend )
        {   // this means a "suspend" is to be "revoked"
            suspendPropertyHandlers_nothrow( false );
            // we ourself cannot revoke our suspend
            return false;
        }
 
        if ( !suspendAll_nothrow() )
            return false;
 
        // commit the editor's content
        if ( haveView() )
            getPropertyBox().CommitModified();
 
        // stop listening
        stopContainerWindowListening();
 
        // outta here
        return true;
    }
 
 
    Any SAL_CALL OPropertyBrowserController::getViewData(  )
    {
        return Any( m_sPageSelection );
    }
 
 
    void SAL_CALL OPropertyBrowserController::restoreViewData( const Any& Data )
    {
        OUString sPageSelection;
        if ( ( Data >>= sPageSelection ) && !sPageSelection.isEmpty() )
        {
            m_sPageSelection = sPageSelection;
            selectPageFromViewData();
        }
    }
 
    Reference< XModel > SAL_CALL OPropertyBrowserController::getModel(  )
    {
        // have no model
        return Reference< XModel >();
    }
 
    Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame(  )
    {
        return m_xFrame;
    }
 
    void SAL_CALL OPropertyBrowserController::dispose()
    {
        SolarMutexGuard aSolarGuard;
 
        // stop inspecting the current object
        stopInspection( false );
 
        // say our dispose listeners goodbye
        css::lang::EventObject aEvt;
        aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
        m_aDisposeListeners.disposeAndClear(aEvt);
        m_aControlObservers.disposeAndClear(aEvt);
 
        m_xPropView.reset();
        m_xBuilder.reset();
 
        if ( m_xView.is() )
            m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
        m_xView.clear( );
 
        m_aInspectedObjects.clear();
        impl_bindToNewModel_nothrow( nullptr );
        m_xModel.clear();
        m_xInteractiveHandler.clear();
        m_xFrame.clear();
    }
 
    void SAL_CALL OPropertyBrowserController::addEventListener( const Reference< XEventListener >& _rxListener )
    {
        m_aDisposeListeners.addInterface(_rxListener);
    }
 
    void SAL_CALL OPropertyBrowserController::removeEventListener( const Reference< XEventListener >& _rxListener )
    {
        m_aDisposeListeners.removeInterface(_rxListener);
    }
 
    OUString SAL_CALL OPropertyBrowserController::getImplementationName(  )
    {
        return u"org.openoffice.comp.extensions.ObjectInspector"_ustr;
    }
 
    sal_Bool SAL_CALL OPropertyBrowserController::supportsService( const OUString& ServiceName )
    {
        return cppu::supportsService(this, ServiceName);
    }
 
 
    Sequence< OUString > SAL_CALL OPropertyBrowserController::getSupportedServiceNames(  )
    {
        return { u"com.sun.star.inspection.ObjectInspector"_ustr };
    }
 
 
    void SAL_CALL OPropertyBrowserController::focusGained( const FocusEvent& _rSource )
    {
        Reference< XWindow > xSourceWindow(_rSource.Source, UNO_QUERY);
        Reference< XWindow > xContainerWindow;
        if (m_xFrame.is())
            xContainerWindow = m_xFrame->getContainerWindow();
 
        if ( xContainerWindow.get() == xSourceWindow.get() )
        {   // our container window got the focus
            if ( haveView() )
                getPropertyBox().GrabFocus();
        }
    }
 
 
    void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ )
    {
        // not interested in
    }
 
 
    void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource )
    {
        if ( m_xView.is() && ( m_xView == _rSource.Source ) )
        {
            m_xView = nullptr;
            m_xPropView.reset();
            m_xBuilder.reset();
        }
 
        auto it = std::find_if(m_aInspectedObjects.begin(), m_aInspectedObjects.end(),
            [&_rSource](const InterfaceArray::value_type& rxObj) { return rxObj == _rSource.Source; });
        if (it != m_aInspectedObjects.end())
            m_aInspectedObjects.erase(it);
    }
 
 
    IMPL_LINK_NOARG(OPropertyBrowserController, OnPageActivation, LinkParamNone*, void)
    {
        updateViewDataFromActivePage();
    }
 
 
    void OPropertyBrowserController::updateViewDataFromActivePage()
    {
        if (!haveView())
            return;
 
        OUString sOldSelection = m_sPageSelection;
        m_sPageSelection.clear();
 
        const sal_uInt16 nCurrentPage = m_xPropView->getActivePage();
        if ( sal_uInt16(-1) != nCurrentPage )
        {
            for (auto const& pageId : m_aPageIds)
            {
                if ( nCurrentPage == pageId.second )
                {
                    m_sPageSelection = pageId.first;
                    break;
                }
            }
        }
 
        if ( !m_sPageSelection.isEmpty() )
            m_sLastValidPageSelection = m_sPageSelection;
        else if ( !sOldSelection.isEmpty() )
            m_sLastValidPageSelection = sOldSelection;
    }
 
 
    sal_uInt16 OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const
    {
        sal_uInt16 nPageId = sal_uInt16(-1);
        HashString2Int16::const_iterator pagePos = m_aPageIds.find( _rCategoryName );
        if ( pagePos != m_aPageIds.end() )
            nPageId = pagePos->second;
        return nPageId;
    }
 
    void OPropertyBrowserController::selectPageFromViewData()
    {
        sal_uInt16 nNewPage = impl_getPageIdForCategory_nothrow( m_sPageSelection );
 
        if ( haveView() && ( nNewPage != sal_uInt16(-1) ) )
            m_xPropView->activatePage( nNewPage );
 
        // just in case ...
        updateViewDataFromActivePage();
    }
 
    void OPropertyBrowserController::Construct(const Reference<XWindow>& rContainerWindow, std::unique_ptr<weld::Builder> xBuilder)
    {
        DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!");
        assert(xBuilder && "OPropertyBrowserController::Construct: invalid parent window!");
 
        m_xBuilder = std::move(xBuilder);
 
        m_xPropView.reset(new OPropertyBrowserView(m_xContext, *m_xBuilder));
        m_xPropView->setPageActivationHandler(LINK(this, OPropertyBrowserController, OnPageActivation));
 
        // add as dispose listener for our view. The view is disposed by the frame we're plugged into,
        // and this disposal _deletes_ the view, so it would be deadly if we use our m_xPropView member
        // after that
        m_xView = rContainerWindow;
        if (m_xView.is())
            m_xView->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
 
        getPropertyBox().SetLineListener(this);
        getPropertyBox().SetControlObserver(this);
        impl_initializeView_nothrow();
    }
 
    void SAL_CALL OPropertyBrowserController::propertyChange( const PropertyChangeEvent& _rEvent )
    {
        if ( _rEvent.Source == m_xModel )
        {
            if ( _rEvent.PropertyName == "IsReadOnly" )
               // this is a huge cudgel, admitted.
                // The problem is that in case we were previously read-only, all our controls
                // were created read-only, too. We cannot simply switch them to not-read-only.
                // Even if they had an API for this, we do not know whether they were
                // originally created read-only, or if they are read-only just because
                // the model was.
                impl_rebindToInspectee_nothrow( std::vector(m_aInspectedObjects) );
            return;
        }
 
        if ( m_sCommittingProperty == _rEvent.PropertyName )
            return;
 
        if ( !haveView() )
            return;
 
        Any aNewValue( _rEvent.NewValue );
        if ( impl_hasPropertyHandlerFor_nothrow( _rEvent.PropertyName ) )
        {
            // forward the new value to the property box, to reflect the change in the UI
            aNewValue = impl_getPropertyValue_throw( _rEvent.PropertyName );
 
            // check whether the state is ambiguous. This is interesting in case we display the properties
            // for multiple objects at once: In this case, we'll get a notification from one of the objects,
            // but need to care for the "composed" value, which can be "ambiguous".
            PropertyHandlerRef xHandler( impl_getHandlerForProperty_throw( _rEvent.PropertyName ), UNO_SET_THROW );
            PropertyState ePropertyState( xHandler->getPropertyState( _rEvent.PropertyName ) );
            bool bAmbiguousValue = ( PropertyState_AMBIGUOUS_VALUE == ePropertyState );
 
            getPropertyBox().SetPropertyValue( _rEvent.PropertyName, aNewValue, bAmbiguousValue );
        }
 
        // if it's an actuating property, then update the UI for any dependent
        // properties
        if ( impl_isActuatingProperty_nothrow( _rEvent.PropertyName ) )
            impl_broadcastPropertyChange_nothrow( _rEvent.PropertyName, aNewValue, _rEvent.OldValue, false );
    }
 
    Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType, sal_Bool bCreateReadOnly )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
 
        Reference< XPropertyControl > xControl;
 
        // read-only-ness
        bCreateReadOnly |= impl_isReadOnlyModel_throw() ? 1 : 0;
 
        switch ( ControlType )
        {
            case PropertyControlType::MultiLineTextField:
            case PropertyControlType::StringListField:
            {
                bool bMultiLineTextField = ControlType == PropertyControlType::MultiLineTextField;
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/multiline.ui"_ustr, m_xContext));
                auto pContainer = xBuilder->weld_container(u"multiline"_ustr);
                rtl::Reference<OMultilineEditControl> pControl = new OMultilineEditControl(std::move(pContainer), std::move(xBuilder),
                                                          bMultiLineTextField ? eMultiLineText : eStringList, bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::ListBox:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/listbox.ui"_ustr, m_xContext));
                auto pComboBox = xBuilder->weld_combo_box(u"listbox"_ustr);
                rtl::Reference<OListboxControl> pControl = new OListboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::ComboBox:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/combobox.ui"_ustr, m_xContext));
                auto pComboBox = xBuilder->weld_combo_box(u"combobox"_ustr);
                rtl::Reference<OComboboxControl> pControl = new OComboboxControl(std::move(pComboBox), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::TextField:
            case PropertyControlType::CharacterField:
            {
                bool bCharacterField = ControlType == PropertyControlType::CharacterField;
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/textfield.ui"_ustr, m_xContext));
                auto pEntry = xBuilder->weld_entry(u"textfield"_ustr);
                rtl::Reference<OEditControl> pControl = new OEditControl(std::move(pEntry), std::move(xBuilder), bCharacterField, bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::NumericField:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/numericfield.ui"_ustr, m_xContext));
                auto pSpinButton = xBuilder->weld_metric_spin_button(u"numericfield"_ustr, FieldUnit::NONE);
                rtl::Reference<ONumericControl> pControl = new ONumericControl(std::move(pSpinButton), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::DateTimeField:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/datetimefield.ui"_ustr, m_xContext));
                auto pContainer = xBuilder->weld_container(u"datetimefield"_ustr);
                rtl::Reference<ODateTimeControl> pControl = new ODateTimeControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::DateField:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/datefield.ui"_ustr, m_xContext));
                auto pContainer = xBuilder->weld_container(u"datefield"_ustr);
                rtl::Reference<ODateControl> pControl = new ODateControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::TimeField:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/timefield.ui"_ustr, m_xContext));
                auto pTimeSpinButton = xBuilder->weld_formatted_spin_button(u"timefield"_ustr);
                rtl::Reference<OTimeControl> pControl = new OTimeControl(std::move(pTimeSpinButton), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::ColorListBox:
            {
                auto lambda = [this]{ return PropertyHandlerHelper::getDialogParentFrame(m_xContext); };
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/colorlistbox.ui"_ustr, m_xContext));
                auto pMenuButton = xBuilder->weld_menu_button(u"colorlistbox"_ustr);
                rtl::Reference<OColorControl> pControl = new OColorControl(std::make_unique<ColorListBox>(std::move(pMenuButton), lambda), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            case PropertyControlType::HyperlinkField:
            {
                std::unique_ptr<weld::Builder> xBuilder(PropertyHandlerHelper::makeBuilder(u"modules/spropctrlr/ui/hyperlinkfield.ui"_ustr, m_xContext));
                auto pContainer = xBuilder->weld_container(u"hyperlinkfield"_ustr);
                rtl::Reference<OHyperlinkControl> pControl = new OHyperlinkControl(std::move(pContainer), std::move(xBuilder), bCreateReadOnly);
                pControl->SetModifyHandler();
                xControl = pControl;
                break;
            }
 
            default:
                throw IllegalArgumentException( OUString(), *this, 1 );
        }
 
        return xControl;
    }
 
 
    void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn )
    {
        for (auto const& inspectedObject : m_aInspectedObjects)
        {
            try
            {
                Reference< XComponent > xComp( inspectedObject, UNO_QUERY );
                if ( xComp.is() )
                {
                    if ( _bOn )
                        xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
                    else
                        xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
                }
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
            }
        }
    }
 
 
    void OPropertyBrowserController::stopInspection( bool _bCommitModified )
    {
        if ( haveView() )
        {
            if ( _bCommitModified )
                // commit the editor's content
                getPropertyBox().CommitModified();
 
            // hide the property box so that it does not flicker
            getPropertyBox().Hide();
 
            // clear the property box
            getPropertyBox().ClearAll();
        }
 
        // destroy the view first
        if ( haveView() )
        {
            // remove the pages
            for (auto const& pageId : m_aPageIds)
                getPropertyBox().RemovePage( pageId.second );
            clearContainer( m_aPageIds );
        }
 
        clearContainer( m_aProperties );
 
        // de-register as dispose-listener from our inspected objects
        impl_toggleInspecteeListening_nothrow( false );
 
        // handlers are obsolete, so is our "composer" for their UI requests
        if (m_pUIRequestComposer)
            m_pUIRequestComposer->dispose();
        m_pUIRequestComposer.reset();
 
        // clean up the property handlers
        PropertyHandlerArray aAllHandlers;  // will contain every handler exactly once
        for (auto const& propertyHandler : m_aPropertyHandlers)
            if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) == aAllHandlers.end() )
                aAllHandlers.push_back( propertyHandler.second );
 
        for (auto const& handler : aAllHandlers)
        {
            try
            {
                handler->removePropertyChangeListener( this );
                handler->dispose();
            }
            catch( const DisposedException& )
            {
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
            }
        }
 
        clearContainer( m_aPropertyHandlers );
        clearContainer( m_aDependencyHandlers );
    }
 
 
    bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const
    {
        PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
        return ( handlerPos != m_aPropertyHandlers.end() );
    }
 
 
    OPropertyBrowserController::PropertyHandlerRef const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const
    {
        PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
        if ( handlerPos == m_aPropertyHandlers.end() )
            throw RuntimeException();
        return handlerPos->second;
    }
 
 
    Any OPropertyBrowserController::impl_getPropertyValue_throw( const OUString& _rPropertyName )
    {
        PropertyHandlerRef handler = impl_getHandlerForProperty_throw( _rPropertyName );
        return handler->getPropertyValue( _rPropertyName );
    }
 
 
    void OPropertyBrowserController::impl_rebindToInspectee_nothrow( InterfaceArray&& _rObjects )
    {
        try
        {
            // stop inspecting the old object(s)
            stopInspection( true );
 
            // inspect the new object(s)
            m_aInspectedObjects = std::move(_rObjects);
            doInspection();
 
            // update the user interface
            UpdateUI();
        }
 
        catch(const Exception&)
        {
            TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
        }
    }
 
 
    void OPropertyBrowserController::doInspection()
    {
        try
        {
 
            // obtain the properties of the object
            std::vector< Property > aProperties;
 
            PropertyHandlerArray aPropertyHandlers;
            getPropertyHandlers( m_aInspectedObjects, aPropertyHandlers );
 
            PropertyHandlerArray::iterator aHandler( aPropertyHandlers.begin() );
            while ( aHandler != aPropertyHandlers.end() )
            {
                DBG_ASSERT( aHandler->get(), "OPropertyBrowserController::doInspection: invalid handler!" );
 
                StlSyntaxSequence< Property > aThisHandlersProperties(  (*aHandler)->getSupportedProperties() );
 
                if ( aThisHandlersProperties.empty() )
                {
                    // this handler doesn't know anything about the current inspectee -> ignore it
                    (*aHandler)->dispose();
                    aHandler = aPropertyHandlers.erase( aHandler );
                    continue;
                }
 
                // append these properties to our "all properties" array
                aProperties.reserve( std::max<size_t>(aProperties.size() + aThisHandlersProperties.size(), aProperties.size() * 2) );
                for (const auto & aThisHandlersProperty : aThisHandlersProperties)
                {
                    auto noPrevious = std::none_of(
                        aProperties.begin(),
                        aProperties.end(),
                        FindPropertyByName( aThisHandlersProperty.Name )
                    );
                    if ( noPrevious )
                    {
                        aProperties.push_back( aThisHandlersProperty );
                        continue;
                    }
 
                    // there already was another (previous) handler which supported this property.
                    // Don't add it to aProperties, again.
 
                    // Also, ensure that handlers which previously expressed interest in *changes*
                    // of this property are not notified.
                    // This is 'cause we have a new handler which is responsible for this property,
                    // which means it can give it a completely different meaning than the previous
                    // handler for this property is prepared for.
                    std::pair< PropertyHandlerMultiRepository::iterator, PropertyHandlerMultiRepository::iterator >
                        aDepHandlers = m_aDependencyHandlers.equal_range( aThisHandlersProperty.Name );
                    m_aDependencyHandlers.erase( aDepHandlers.first, aDepHandlers.second );
                }
 
                // determine the superseded properties
                StlSyntaxSequence< OUString > aSupersededByThisHandler( (*aHandler)->getSupersededProperties() );
                for (const auto & superseded : aSupersededByThisHandler)
                {
                    std::vector< Property >::iterator existent = std::find_if(
                        aProperties.begin(),
                        aProperties.end(),
                        FindPropertyByName( superseded )
                    );
                    if ( existent != aProperties.end() )
                        // one of the properties superseded by this handler was supported by a previous
                        // one -> erase
                        aProperties.erase( existent );
                }
 
                // be notified of changes which this handler is responsible for
                (*aHandler)->addPropertyChangeListener( this );
 
                // remember this handler for every of the properties which it is responsible
                // for
                for (const auto & aThisHandlersProperty : aThisHandlersProperties)
                {
                    m_aPropertyHandlers[ aThisHandlersProperty.Name ] = *aHandler;
                    // note that this implies that if two handlers support the same property,
                    // the latter wins
                }
 
                // see if the handler expresses interest in any actuating properties
                StlSyntaxSequence< OUString > aInterestingActuations( (*aHandler)->getActuatingProperties() );
                for (const auto & aInterestingActuation : aInterestingActuations)
                {
                    m_aDependencyHandlers.emplace( aInterestingActuation, *aHandler );
                }
 
                ++aHandler;
            }
 
            // create a new composer for UI requests coming from the handlers
            m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) );
 
            // sort the properties by relative position, as indicated by the model
            sal_Int32 nPos = 0;
            for (auto const& sourceProps : aProperties)
            {
                sal_Int32 nRelativePropertyOrder = nPos;
                if ( m_xModel.is() )
                    nRelativePropertyOrder = m_xModel->getPropertyOrderIndex( sourceProps.Name );
                m_aProperties.emplace(nRelativePropertyOrder, sourceProps);
                ++nPos;
            }
 
            // be notified when one of our inspectees dies
            impl_toggleInspecteeListening_nothrow( true );
        }
        catch(const Exception&)
        {
            TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
        }
    }
 
 
    css::awt::Size SAL_CALL OPropertyBrowserController::getMinimumSize()
    {
        css::awt::Size aSize;
        if( m_xPropView )
            return m_xPropView->getMinimumSize();
        else
            return aSize;
    }
 
 
    css::awt::Size SAL_CALL OPropertyBrowserController::getPreferredSize()
    {
        return getMinimumSize();
    }
 
 
    css::awt::Size SAL_CALL OPropertyBrowserController::calcAdjustedSize( const css::awt::Size& _rNewSize )
    {
        awt::Size aMinSize = getMinimumSize( );
        awt::Size aAdjustedSize( _rNewSize );
        if ( aAdjustedSize.Width < aMinSize.Width )
            aAdjustedSize.Width = aMinSize.Width;
        if ( aAdjustedSize.Height < aMinSize.Height )
            aAdjustedSize.Height = aMinSize.Height;
        return aAdjustedSize;
    }
 
 
    void OPropertyBrowserController::describePropertyLine( const Property& _rProperty, OLineDescriptor& _rDescriptor )
    {
        try
        {
            PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rProperty.Name );
            if ( handler == m_aPropertyHandlers.end() )
                throw RuntimeException();   // caught below
 
            _rDescriptor.assignFrom( handler->second->describePropertyLine( _rProperty.Name, this ) );
 
 
            _rDescriptor.xPropertyHandler = handler->second;
            _rDescriptor.sName = _rProperty.Name;
            _rDescriptor.aValue = _rDescriptor.xPropertyHandler->getPropertyValue( _rProperty.Name );
 
            if ( _rDescriptor.DisplayName.isEmpty() )
            {
            #ifdef DBG_UTIL
                SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
                        <<_rProperty.Name << "'!" );
            #endif
                _rDescriptor.DisplayName = _rProperty.Name;
            }
 
            PropertyState   ePropertyState( _rDescriptor.xPropertyHandler->getPropertyState( _rProperty.Name ) );
            if ( PropertyState_AMBIGUOUS_VALUE == ePropertyState )
            {
                _rDescriptor.bUnknownValue = true;
                _rDescriptor.aValue.clear();
            }
 
            _rDescriptor.bReadOnly = impl_isReadOnlyModel_throw();
 
            // for ui-testing try and distinguish different instances of the controls
            auto xWindow = _rDescriptor.Control->getControlWindow();
            if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xWindow.get()))
            {
                weld::Widget* m_pControlWindow = pTunnel->getWidget();
                if (m_pControlWindow)
                    m_pControlWindow->set_buildable_name(m_pControlWindow->get_buildable_name() + "-" + _rDescriptor.DisplayName);
            }
 
        }
        catch( const Exception& )
        {
            TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine" );
        }
    }
 
 
    void OPropertyBrowserController::impl_buildCategories_throw()
    {
        OSL_PRECOND( m_aPageIds.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" );
 
        StlSyntaxSequence< PropertyCategoryDescriptor > aCategories;
        if ( m_xModel.is() )
            aCategories = StlSyntaxSequence< PropertyCategoryDescriptor >(m_xModel->describeCategories());
 
        for (auto const& category : aCategories)
        {
            OSL_ENSURE( m_aPageIds.find( category.ProgrammaticName ) == m_aPageIds.end(),
                "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" );
 
            m_aPageIds[ category.ProgrammaticName ] =
                getPropertyBox().AppendPage( category.UIName, HelpIdUrl::getHelpId( category.HelpURL ) );
        }
    }
 
 
    void OPropertyBrowserController::UpdateUI()
    {
        try
        {
            if ( !haveView() )
                // too early, will return later
                return;
 
            // create our tab pages
            impl_buildCategories_throw();
            // (and allow for pages to be actually unused)
            std::set< sal_uInt16 > aUsedPages;
 
            // when building the UI below, remember which properties are actuating,
            // to allow for an initial actuatingPropertyChanged call
            std::vector< OUString > aActuatingProperties;
            std::vector< Any > aActuatingPropertyValues;
 
            // ask the handlers to describe the property UI, and insert the resulting
            // entries into our list boxes
            for (auto const& property : m_aProperties)
            {
                OLineDescriptor aDescriptor;
                describePropertyLine( property.second, aDescriptor );
 
                bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( property.second.Name );
 
                SAL_WARN_IF( aDescriptor.Category.isEmpty(), "extensions.propctrlr",
                        "OPropertyBrowserController::UpdateUI: empty category provided for property '"
                        << property.second.Name << "'!");
                // finally insert this property control
                sal_uInt16 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
                if ( nTargetPageId == sal_uInt16(-1) )
                {
                    // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide
                    // any category information of its own. In this case, we have a fallback ...
                    m_aPageIds[ aDescriptor.Category ] =
                        getPropertyBox().AppendPage(aDescriptor.Category, {});
                    nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
                }
 
                getPropertyBox().InsertEntry( aDescriptor, nTargetPageId );
                aUsedPages.insert( nTargetPageId );
 
                // if it's an actuating property, remember it
                if ( bIsActuatingProperty )
                {
                    aActuatingProperties.push_back( property.second.Name );
                    aActuatingPropertyValues.push_back( impl_getPropertyValue_throw( property.second.Name ) );
                }
            }
 
            // update any dependencies for the actuating properties which we encountered
            {
                std::vector< Any >::const_iterator aPropertyValue = aActuatingPropertyValues.begin();
                for (auto const& actuatingProperty : aActuatingProperties)
                {
                    impl_broadcastPropertyChange_nothrow( actuatingProperty, *aPropertyValue, *aPropertyValue, true );
                    ++aPropertyValue;
                }
            }
 
            // remove any unused pages (which we did not encounter properties for)
            HashString2Int16 aSurvivingPageIds;
            for (auto const& pageId : m_aPageIds)
            {
                if ( aUsedPages.find( pageId.second ) == aUsedPages.end() )
                    getPropertyBox().RemovePage( pageId.second );
                else
                    aSurvivingPageIds.insert(pageId);
            }
            m_aPageIds.swap( aSurvivingPageIds );
 
            getPropertyBox().Show();
 
            // activate the first page
            if ( !m_aPageIds.empty() )
            {
                Sequence< PropertyCategoryDescriptor > aCategories( m_xModel->describeCategories() );
                if ( aCategories.hasElements() )
                    m_xPropView->activatePage( m_aPageIds[ aCategories[0].ProgrammaticName ] );
                else
                    // allowed: if we default-created the pages ...
                    m_xPropView->activatePage( m_aPageIds.begin()->second );
            }
 
            // activate the previously active page (if possible)
            if ( !m_sLastValidPageSelection.isEmpty() )
                m_sPageSelection = m_sLastValidPageSelection;
            selectPageFromViewData();
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }
 
 
    void OPropertyBrowserController::Clicked( const OUString& _rName, bool _bPrimary )
    {
        try
        {
            // since the browse buttons do not get the focus when clicked with the mouse,
            // we need to commit the changes in the current property field
            getPropertyBox().CommitModified();
 
            PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rName );
            DBG_ASSERT( handler != m_aPropertyHandlers.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" );
 
            ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
 
            Any aData;
            m_xInteractiveHandler = handler->second;
            InteractiveSelectionResult eResult =
                handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData,
                    m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) );
 
            switch ( eResult )
            {
            case InteractiveSelectionResult_Cancelled:
            case InteractiveSelectionResult_Success:
                // okay, nothing to do
                break;
            case InteractiveSelectionResult_ObtainedValue:
                handler->second->setPropertyValue( _rName, aData );
                break;
            case InteractiveSelectionResult_Pending:
                // also okay, we expect that the handler has disabled the UI as necessary
                break;
            default:
                OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
                break;
            }
        }
        catch (const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
        m_xInteractiveHandler = nullptr;
    }
 
 
    bool OPropertyBrowserController::hasPropertyByName( const OUString& _rName )
    {
        for (auto const& property : m_aProperties)
            if ( property.second.Name == _rName )
                return true;
        return false;
    }
 
 
    void OPropertyBrowserController::Commit( const OUString& rName, const Any& _rValue )
    {
        try
        {
            OUString sPlcHolder = PcrRes(RID_EMBED_IMAGE_PLACEHOLDER);
            bool bIsPlaceHolderValue = false;
 
            if ( rName == PROPERTY_IMAGE_URL )
            {
                // if the prop value is the PlaceHolder
                // can ignore it
                OUString sVal;
                _rValue >>= sVal;
                if ( sVal == sPlcHolder )
                    bIsPlaceHolderValue = true;
            }
            m_sCommittingProperty = rName;
 
            bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName );
 
            Any aOldValue;
            if ( bIsActuatingProperty )
                aOldValue = impl_getPropertyValue_throw( rName );
 
            // do we have a dedicated handler for this property, which we can delegate some tasks to?
            PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
 
 
            // set the value ( only if it's not a placeholder )
            if ( !bIsPlaceHolderValue )
                handler->setPropertyValue( rName, _rValue );
 
 
            // re-retrieve the value
            Any aNormalizedValue = handler->getPropertyValue( rName );
 
            // care for any inter-property dependencies
            if ( bIsActuatingProperty )
                impl_broadcastPropertyChange_nothrow( rName, aNormalizedValue, aOldValue, false );
 
            // and display it again. This ensures proper formatting
            getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
        }
        catch(const PropertyVetoException& eVetoException)
        {
            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xPropView->getPropertyBox().getWidget(),
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          eVetoException.Message));
            xInfoBox->run();
            PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
            Any aNormalizedValue = handler->getPropertyValue( rName );
            getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
        }
        catch(const Exception&)
        {
            TOOLS_WARN_EXCEPTION("extensions.propctrlr", "");
        }
 
        m_sCommittingProperty.clear();
    }
 
 
    void OPropertyBrowserController::focusGained( const Reference< XPropertyControl >& Control )
    {
        m_aControlObservers.notifyEach( &XPropertyControlObserver::focusGained, Control );
    }
 
 
    void OPropertyBrowserController::valueChanged( const Reference< XPropertyControl >& Control )
    {
        m_aControlObservers.notifyEach( &XPropertyControlObserver::valueChanged, Control );
    }
 
 
    namespace
    {
        Reference< XPropertyHandler > lcl_createHandler( const Reference<XComponentContext>& _rContext, const Any& _rFactoryDescriptor )
        {
            Reference< XPropertyHandler > xHandler;
 
            OUString sServiceName;
            Reference< XSingleServiceFactory > xServiceFac;
            Reference< XSingleComponentFactory > xComponentFac;
 
            if ( _rFactoryDescriptor >>= sServiceName )
                xHandler.set( _rContext->getServiceManager()->createInstanceWithContext( sServiceName, _rContext ), UNO_QUERY );
            else if ( _rFactoryDescriptor >>= xServiceFac )
                xHandler.set(xServiceFac->createInstance(), css::uno::UNO_QUERY);
            else if ( _rFactoryDescriptor >>= xComponentFac )
                xHandler.set(xComponentFac->createInstanceWithContext( _rContext ), css::uno::UNO_QUERY);
            OSL_ENSURE(xHandler.is(),"lcl_createHandler: Can not create handler");
            return xHandler;
        }
    }
 
 
    void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers )
    {
        _rHandlers.resize( 0 );
        if ( _rObjects.empty() )
            return;
 
        Sequence< Any > aHandlerFactories;
        if ( m_xModel.is() )
            aHandlerFactories = m_xModel->getHandlerFactories();
 
        for (auto const& handlerFactory : aHandlerFactories)
        {
            if ( _rObjects.size() == 1 )
            {   // we're inspecting only one object -> one handler
                Reference< XPropertyHandler > xHandler( lcl_createHandler( m_xContext, handlerFactory ) );
                if ( xHandler.is() )
                {
                    xHandler->inspect( _rObjects[0] );
                    _rHandlers.push_back( xHandler );
                }
            }
            else
            {
                // create a single handler for every single object
                std::vector< Reference< XPropertyHandler > > aSingleHandlers( _rObjects.size() );
                std::vector< Reference< XPropertyHandler > >::iterator pHandler = aSingleHandlers.begin();
 
                for (auto const& elem : _rObjects)
                {
                    *pHandler = lcl_createHandler( m_xContext, handlerFactory );
                    if ( pHandler->is() )
                    {
                        (*pHandler)->inspect(elem);
                        ++pHandler;
                    }
                }
                aSingleHandlers.resize( pHandler - aSingleHandlers.begin() );
 
                // then create a handler which composes information out of those single handlers
                if ( !aSingleHandlers.empty() )
                    _rHandlers.push_back( new PropertyComposer( std::move(aSingleHandlers) ) );
            }
        }
 
        // note that the handlers will not be used by our caller, if they indicate that there are no
        // properties they feel responsible for
    }
 
 
    bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty )
    {
        OrderedPropertyMap::const_iterator search = std::find_if(m_aProperties.begin(), m_aProperties.end(),
            [&_rName](const OrderedPropertyMap::value_type& rEntry) { return rEntry.second.Name == _rName; });
        if ( _pProperty )
            *_pProperty = search;
        return ( search != m_aProperties.end() );
    }
 
 
    void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        OrderedPropertyMap::const_iterator propertyPos;
        if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
            return;
 
        OLineDescriptor aDescriptor;
        try
        {
            describePropertyLine( propertyPos->second, aDescriptor );
        }
        catch( const Exception& )
        {
            TOOLS_WARN_EXCEPTION( "extensions.propctrlr", "OPropertyBrowserController::rebuildPropertyUI" );
        }
 
        getPropertyBox().ChangeEntry( aDescriptor );
   }
 
 
    void OPropertyBrowserController::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
            return;
 
        getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable );
    }
 
 
    void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
            return;
 
        getPropertyBox().EnablePropertyControls( _rPropertyName, _nElements, _bEnable );
    }
 
 
    void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        // look up the property in our object properties
        OrderedPropertyMap::const_iterator propertyPos;
        if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
            return;
 
        if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND )
        {
            rebuildPropertyUI( _rPropertyName );
            return;
        }
 
        OLineDescriptor aDescriptor;
        describePropertyLine( propertyPos->second, aDescriptor );
 
        // look for the position to insert the property
 
        // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work
        // only on the current page. This implies that it's impossible to use this method here
        // to show property lines which are *not* on the current page.
        // This is sufficient for now, but should be changed in the future.
 
        // by definition, the properties in m_aProperties are in the order in which they appear in the UI
        // So all we need is a predecessor of pProperty in m_aProperties
        sal_uInt16 nUIPos = EDITOR_LIST_ENTRY_NOTFOUND;
        do
        {
            if ( propertyPos != m_aProperties.begin() )
                --propertyPos;
            nUIPos = getPropertyBox().GetPropertyPos( propertyPos->second.Name );
        }
        while ( ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) && ( propertyPos != m_aProperties.begin() ) );
 
        if ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND )
            // insert at the very top
            nUIPos = 0;
        else
            // insert right after the predecessor we found
            ++nUIPos;
 
        getPropertyBox().InsertEntry(
            aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos );
    }
 
 
    void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
            return;
 
        getPropertyBox().RemoveEntry( _rPropertyName );
    }
 
 
    void OPropertyBrowserController::showCategory( const OUString& rCategory, sal_Bool bShow )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        sal_uInt16 nPageId = impl_getPageIdForCategory_nothrow( rCategory );
        OSL_ENSURE( nPageId != sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" );
 
        getPropertyBox().ShowPropertyPage( nPageId, bShow );
    }
 
 
    Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::getPropertyControl( const OUString& _rPropertyName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !haveView() )
            throw RuntimeException();
 
        Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) );
        return xControl;
    }
 
 
    void SAL_CALL OPropertyBrowserController::registerControlObserver( const Reference< XPropertyControlObserver >& Observer )
    {
        m_aControlObservers.addInterface( Observer );
    }
 
 
    void SAL_CALL OPropertyBrowserController::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer )
    {
        m_aControlObservers.removeInterface( Observer );
    }
 
 
    void SAL_CALL OPropertyBrowserController::setHelpSectionText( const OUString& _rHelpText )
    {
        SolarMutexGuard aSolarGuard;
        ::osl::MutexGuard aGuard( m_aMutex );
 
        if ( !haveView() )
            throw DisposedException();
 
        if ( !getPropertyBox().HasHelpSection() )
            throw NoSupportException();
 
        getPropertyBox().SetHelpText( _rHelpText );
    }
 
 
    void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const Any& _rNewValue, const Any& _rOldValue, bool _bFirstTimeInit ) const
    {
        // are there one or more handlers which are interested in the actuation?
        std::pair< PropertyHandlerMultiRepository::const_iterator, PropertyHandlerMultiRepository::const_iterator > aInterestedHandlers =
            m_aDependencyHandlers.equal_range( _rPropertyName );
        if ( aInterestedHandlers.first == aInterestedHandlers.second )
            // none of our handlers is interested in this
            return;
 
        ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
        try
        {
            // collect the responses from all interested handlers
            PropertyHandlerMultiRepository::const_iterator handler = aInterestedHandlers.first;
            while ( handler != aInterestedHandlers.second )
            {
                handler->second->actuatingPropertyChanged( _rPropertyName, _rNewValue, _rOldValue,
                    m_pUIRequestComposer->getUIForPropertyHandler( handler->second ),
                    _bFirstTimeInit );
                ++handler;
            }
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }
 
 
} // namespace pcr
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
extensions_propctrlr_OPropertyBrowserController_get_implementation(
    css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
{
    return cppu::acquire(new pcr::OPropertyBrowserController(context));
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1053 Calling the 'acquire' virtual function in the destructor may lead to unexpected result at runtime.

V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 829, 843.