1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "pcrservices.hxx"
21 #include "propcontroller.hxx"
22 #include "pcrstrings.hxx"
23 #include "standardcontrol.hxx"
24 #include "linedescriptor.hxx"
25 #include <strings.hrc>
26 #include "propertyeditor.hxx"
27 #include "modulepcr.hxx"
28 #include "formstrings.hxx"
29 #include "formmetadata.hxx"
30 #include "formbrowsertools.hxx"
31 #include "propertycomposer.hxx"
32 
33 #include <com/sun/star/awt/XWindow.hpp>
34 #include <com/sun/star/lang/NoSupportException.hpp>
35 #include <com/sun/star/util/XCloseable.hpp>
36 #include <com/sun/star/inspection/PropertyControlType.hpp>
37 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
38 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
39 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 #include <com/sun/star/util/VetoException.hpp>
41 #include <tools/debug.hxx>
42 #include <tools/diagnose_ex.h>
43 #include <comphelper/types.hxx>
44 #include <toolkit/awt/vclxwindow.hxx>
45 #include <toolkit/helper/vclunohelper.hxx>
46 #include <comphelper/property.hxx>
47 #include <vcl/weld.hxx>
48 #include <vcl/svapp.hxx>
49 #include <osl/mutex.hxx>
50 #include <cppuhelper/queryinterface.hxx>
51 #include <cppuhelper/component_context.hxx>
52 #include <cppuhelper/exc_hlp.hxx>
53 #include <cppuhelper/supportsservice.hxx>
54 
55 #include <algorithm>
56 #include <functional>
57 #include <sal/macros.h>
58 #include <sal/log.hxx>
59 
60 
61 // !!! outside the namespace !!!
createRegistryInfo_OPropertyBrowserController()62 extern "C" void createRegistryInfo_OPropertyBrowserController()
63 {
64     ::pcr::OAutoRegistration< ::pcr::OPropertyBrowserController > aAutoRegistration;
65 }
66 
67 
68 namespace pcr
69 {
70 
71 
72     using namespace ::com::sun::star;
73     using namespace ::com::sun::star::uno;
74     using namespace ::com::sun::star::awt;
75     using namespace ::com::sun::star::form;
76     using namespace ::com::sun::star::beans;
77     using namespace ::com::sun::star::script;
78     using namespace ::com::sun::star::lang;
79     using namespace ::com::sun::star::container;
80     using namespace ::com::sun::star::frame;
81     using namespace ::com::sun::star::util;
82     using namespace ::com::sun::star::inspection;
83     using namespace ::com::sun::star::ucb;
84     using namespace ::comphelper;
85 
86 
87     //= OPropertyBrowserController
88 
89 
OPropertyBrowserController(const Reference<XComponentContext> & _rxContext)90     OPropertyBrowserController::OPropertyBrowserController( const Reference< XComponentContext >& _rxContext )
91             :m_xContext(_rxContext)
92             ,m_aDisposeListeners( m_aMutex )
93             ,m_aControlObservers( m_aMutex )
94             ,m_pView(nullptr)
95             ,m_bContainerFocusListening( false )
96             ,m_bSuspendingPropertyHandlers( false )
97             ,m_bConstructed( false )
98             ,m_bBindingIntrospectee( false )
99     {
100     }
101 
102 
~OPropertyBrowserController()103     OPropertyBrowserController::~OPropertyBrowserController()
104     {
105         // stop listening for property changes
106         acquire();
107         stopInspection( true );
108     }
109 
110 
IMPLEMENT_FORWARD_REFCOUNT(OPropertyBrowserController,OPropertyBrowserController_Base)111     IMPLEMENT_FORWARD_REFCOUNT( OPropertyBrowserController, OPropertyBrowserController_Base )
112 
113 
114     Any SAL_CALL OPropertyBrowserController::queryInterface( const Type& _rType )
115     {
116         Any aReturn = OPropertyBrowserController_Base::queryInterface( _rType );
117         if ( !aReturn.hasValue() )
118             aReturn = ::cppu::queryInterface(
119                 _rType,
120                 static_cast< XObjectInspectorUI* >( this )
121             );
122         return aReturn;
123     }
124 
125 
startContainerWindowListening()126     void OPropertyBrowserController::startContainerWindowListening()
127     {
128         if (m_bContainerFocusListening)
129             return;
130 
131         if (m_xFrame.is())
132         {
133             Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
134             if (xContainerWindow.is())
135             {
136                 xContainerWindow->addFocusListener(this);
137                 m_bContainerFocusListening = true;
138             }
139         }
140 
141         DBG_ASSERT(m_bContainerFocusListening, "OPropertyBrowserController::startContainerWindowListening: unable to start listening (inconsistence)!");
142     }
143 
144 
stopContainerWindowListening()145     void OPropertyBrowserController::stopContainerWindowListening()
146     {
147         if (!m_bContainerFocusListening)
148             return;
149 
150         if (m_xFrame.is())
151         {
152             Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
153             if (xContainerWindow.is())
154             {
155                 xContainerWindow->removeFocusListener(this);
156                 m_bContainerFocusListening = false;
157             }
158         }
159 
160         DBG_ASSERT(!m_bContainerFocusListening, "OPropertyBrowserController::stopContainerWindowListening: unable to stop listening (inconsistence)!");
161     }
162 
163 
getInspectorModel()164     Reference< XObjectInspectorModel > SAL_CALL OPropertyBrowserController::getInspectorModel()
165     {
166         return m_xModel;
167     }
168 
169 
impl_initializeView_nothrow()170     void OPropertyBrowserController::impl_initializeView_nothrow()
171     {
172         OSL_PRECOND( haveView(), "OPropertyBrowserController::impl_initializeView_nothrow: not to be called when we have no view!" );
173         if ( !haveView() )
174             return;
175 
176         if ( !m_xModel.is() )
177             // allowed
178             return;
179 
180         try
181         {
182             getPropertyBox().EnableHelpSection( m_xModel->getHasHelpSection() );
183             getPropertyBox().SetHelpLineLimites( m_xModel->getMinHelpTextLines(), m_xModel->getMaxHelpTextLines() );
184         }
185         catch( const Exception& )
186         {
187             DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
188         }
189     }
190 
191 
impl_isReadOnlyModel_throw() const192     bool OPropertyBrowserController::impl_isReadOnlyModel_throw() const
193     {
194         if ( !m_xModel.is() )
195             return false;
196 
197         return m_xModel->getIsReadOnly();
198     }
199 
200 
impl_startOrStopModelListening_nothrow(bool _bDoListen) const201     void OPropertyBrowserController::impl_startOrStopModelListening_nothrow( bool _bDoListen ) const
202     {
203         try
204         {
205             Reference< XPropertySet > xModelProperties( m_xModel, UNO_QUERY );
206             if ( !xModelProperties.is() )
207                 // okay, so the model doesn't want to change its properties
208                 // dynamically - fine with us
209                 return;
210 
211             void (SAL_CALL XPropertySet::*pListenerOperation)( const OUString&, const Reference< XPropertyChangeListener >& )
212                 = _bDoListen ? &XPropertySet::addPropertyChangeListener : &XPropertySet::removePropertyChangeListener;
213 
214             (xModelProperties.get()->*pListenerOperation)(
215                 OUString(  "IsReadOnly"  ),
216                 const_cast< OPropertyBrowserController* >( this )
217             );
218         }
219         catch( const Exception& )
220         {
221             DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
222         }
223     }
224 
225 
impl_bindToNewModel_nothrow(const Reference<XObjectInspectorModel> & _rxInspectorModel)226     void OPropertyBrowserController::impl_bindToNewModel_nothrow( const Reference< XObjectInspectorModel >& _rxInspectorModel )
227     {
228         impl_startOrStopModelListening_nothrow( false );
229         m_xModel = _rxInspectorModel;
230         impl_startOrStopModelListening_nothrow( true );
231 
232         // initialize the view, if we already have one
233         if ( haveView() )
234             impl_initializeView_nothrow();
235 
236         // inspect again, if we already have inspectees
237         if ( !m_aInspectedObjects.empty() )
238             impl_rebindToInspectee_nothrow( m_aInspectedObjects );
239     }
240 
241 
setInspectorModel(const Reference<XObjectInspectorModel> & _inspectorModel)242     void SAL_CALL OPropertyBrowserController::setInspectorModel( const Reference< XObjectInspectorModel >& _inspectorModel )
243     {
244         ::osl::MutexGuard aGuard( m_aMutex );
245 
246         if ( m_xModel == _inspectorModel )
247             return;
248 
249         impl_bindToNewModel_nothrow( _inspectorModel );
250     }
251 
252 
getInspectorUI()253     Reference< XObjectInspectorUI > SAL_CALL OPropertyBrowserController::getInspectorUI()
254     {
255         // we're derived from this interface, though we do not expose it in queryInterface and getTypes.
256         return this;
257     }
258 
259 
inspect(const Sequence<Reference<XInterface>> & _rObjects)260     void SAL_CALL OPropertyBrowserController::inspect( const Sequence< Reference< XInterface > >& _rObjects )
261     {
262         SolarMutexGuard aSolarGuard;
263         ::osl::MutexGuard aGuard( m_aMutex );
264 
265         if ( m_bSuspendingPropertyHandlers || !suspendAll_nothrow() )
266         {   // we already are trying to suspend the component (this is somewhere up the stack)
267             // OR one of our property handlers raised a veto against closing. Well, we *need* to close
268             // it in order to inspect another object.
269             throw VetoException();
270         }
271         if ( m_bBindingIntrospectee )
272             throw VetoException();
273 
274         m_bBindingIntrospectee = true;
275         impl_rebindToInspectee_nothrow( InterfaceArray( _rObjects.begin(), _rObjects.end() ) );
276         m_bBindingIntrospectee = false;
277 
278     }
279 
280 
queryDispatch(const URL &,const OUString &,::sal_Int32)281     Reference< XDispatch > SAL_CALL OPropertyBrowserController::queryDispatch( const URL& /*URL*/, const OUString& /*TargetFrameName*/, ::sal_Int32 /*SearchFlags*/ )
282     {
283         // we don't have any dispatches at all, right now
284         return Reference< XDispatch >();
285     }
286 
287 
queryDispatches(const Sequence<DispatchDescriptor> & Requests)288     Sequence< Reference< XDispatch > > SAL_CALL OPropertyBrowserController::queryDispatches( const Sequence< DispatchDescriptor >& Requests )
289     {
290         Sequence< Reference< XDispatch > > aReturn;
291         sal_Int32 nLen = Requests.getLength();
292         aReturn.realloc( nLen );
293 
294         Reference< XDispatch >* pReturn     = aReturn.getArray();
295         const   Reference< XDispatch >* pReturnEnd  = aReturn.getArray() + nLen;
296         const   DispatchDescriptor*     pDescripts  = Requests.getConstArray();
297 
298         for ( ; pReturn != pReturnEnd; ++ pReturn, ++pDescripts )
299             *pReturn = queryDispatch( pDescripts->FeatureURL, pDescripts->FrameName, pDescripts->SearchFlags );
300 
301         return aReturn;
302     }
303 
304 
initialize(const Sequence<Any> & _arguments)305     void SAL_CALL OPropertyBrowserController::initialize( const Sequence< Any >& _arguments )
306     {
307         if ( m_bConstructed )
308             throw AlreadyInitializedException();
309 
310         StlSyntaxSequence< Any > arguments( _arguments );
311         if ( arguments.empty() )
312         {   // constructor: "createDefault()"
313             m_bConstructed = true;
314             return;
315         }
316 
317         Reference< XObjectInspectorModel > xModel;
318         if ( arguments.size() == 1 )
319         {   // constructor: "createWithModel( XObjectInspectorModel )"
320             if ( !( arguments[0] >>= xModel ) )
321                 throw IllegalArgumentException( OUString(), *this, 0 );
322             createWithModel( xModel );
323             return;
324         }
325 
326         throw IllegalArgumentException( OUString(), *this, 0 );
327     }
328 
329 
createWithModel(const Reference<XObjectInspectorModel> & _rxModel)330     void OPropertyBrowserController::createWithModel( const Reference< XObjectInspectorModel >& _rxModel )
331     {
332         osl_atomic_increment( &m_refCount );
333         {
334             setInspectorModel( _rxModel );
335         }
336         osl_atomic_decrement( &m_refCount );
337 
338         m_bConstructed = true;
339     }
340 
341 
attachFrame(const Reference<XFrame> & _rxFrame)342     void SAL_CALL OPropertyBrowserController::attachFrame( const Reference< XFrame >& _rxFrame )
343     {
344         SolarMutexGuard aSolarGuard;
345         ::osl::MutexGuard aGuard( m_aMutex );
346 
347         if (_rxFrame.is() && haveView())
348             throw RuntimeException("Unable to attach to a second frame.",*this);
349 
350         // revoke as focus listener from the old container window
351         stopContainerWindowListening();
352 
353         m_xFrame = _rxFrame;
354         if (!m_xFrame.is())
355             return;
356 
357         // TODO: this construction perhaps should be done outside. Don't know the exact meaning of attachFrame.
358         // Maybe it is intended to only announce the frame to the controller, and the instance doing this
359         // announcement is responsible for calling setComponent, too.
360         Reference< XWindow > xContainerWindow = m_xFrame->getContainerWindow();
361         VCLXWindow* pContainerWindow = comphelper::getUnoTunnelImplementation<VCLXWindow>(xContainerWindow);
362         VclPtr<vcl::Window> pParentWin = pContainerWindow ? pContainerWindow->GetWindow() : VclPtr<vcl::Window>();
363         if (!pParentWin)
364             throw RuntimeException("The frame is invalid. Unable to extract the container window.",*this);
365 
366         Construct( pParentWin );
367         try
368         {
369             m_xFrame->setComponent( VCLUnoHelper::GetInterface( m_pView ), this );
370         }
371         catch( const Exception& )
372         {
373             OSL_FAIL( "OPropertyBrowserController::attachFrame: caught an exception!" );
374         }
375 
376         startContainerWindowListening();
377 
378         UpdateUI();
379     }
380 
381 
attachModel(const Reference<XModel> & _rxModel)382     sal_Bool SAL_CALL OPropertyBrowserController::attachModel( const Reference< XModel >& _rxModel )
383     {
384         Reference< XObjectInspectorModel > xModel( _rxModel, UNO_QUERY );
385         if ( !xModel.is() )
386             return false;
387 
388         setInspectorModel( xModel );
389         return getInspectorModel() == _rxModel;
390     }
391 
392 
suspendAll_nothrow()393     bool OPropertyBrowserController::suspendAll_nothrow()
394     {
395         // if there is a handle inside its "onInteractivePropertySelection" method,
396         // then veto
397         // Normally, we could expect every handler to do this itself, but being
398         // realistic, it's safer to handle this here in general.
399         if ( m_xInteractiveHandler.is() )
400             return false;
401 
402         m_bSuspendingPropertyHandlers = true;
403         bool bHandlerVeto = !suspendPropertyHandlers_nothrow( true );
404         m_bSuspendingPropertyHandlers = false;
405         return !bHandlerVeto;
406     }
407 
408 
suspendPropertyHandlers_nothrow(bool _bSuspend)409     bool OPropertyBrowserController::suspendPropertyHandlers_nothrow( bool _bSuspend )
410     {
411         PropertyHandlerArray aAllHandlers;  // will contain every handler exactly once
412         for (auto const& propertyHandler : m_aPropertyHandlers)
413         {
414             if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) != aAllHandlers.end() )
415                 // already visited this particular handler (m_aPropertyHandlers usually contains
416                 // the same handler more than once)
417                 continue;
418             aAllHandlers.push_back(propertyHandler.second);
419         }
420 
421         for (auto const& handler : aAllHandlers)
422         {
423             try
424             {
425                 if ( !handler->suspend( _bSuspend ) )
426                     if ( _bSuspend )
427                         // if we're not suspending, but reactivating, ignore the error
428                         return false;
429             }
430             catch( const Exception& )
431             {
432                 OSL_FAIL( "OPropertyBrowserController::suspendPropertyHandlers_nothrow: caught an exception!" );
433             }
434         }
435         return true;
436     }
437 
438 
suspend(sal_Bool _bSuspend)439     sal_Bool SAL_CALL OPropertyBrowserController::suspend( sal_Bool _bSuspend )
440     {
441         ::osl::MutexGuard aGuard( m_aMutex );
442         OSL_ENSURE( haveView(), "OPropertyBrowserController::suspend: don't have a view anymore!" );
443 
444         if ( !_bSuspend )
445         {   // this means a "suspend" is to be "revoked"
446             suspendPropertyHandlers_nothrow( false );
447             // we ourself cannot revoke our suspend
448             return false;
449         }
450 
451         if ( !suspendAll_nothrow() )
452             return false;
453 
454         // commit the editor's content
455         if ( haveView() )
456             getPropertyBox().CommitModified();
457 
458         // stop listening
459         stopContainerWindowListening();
460 
461         // outta here
462         return true;
463     }
464 
465 
getViewData()466     Any SAL_CALL OPropertyBrowserController::getViewData(  )
467     {
468         return makeAny( m_sPageSelection );
469     }
470 
471 
restoreViewData(const Any & Data)472     void SAL_CALL OPropertyBrowserController::restoreViewData( const Any& Data )
473     {
474         OUString sPageSelection;
475         if ( ( Data >>= sPageSelection ) && !sPageSelection.isEmpty() )
476         {
477             m_sPageSelection = sPageSelection;
478             selectPageFromViewData();
479         }
480     }
481 
482 
getModel()483     Reference< XModel > SAL_CALL OPropertyBrowserController::getModel(  )
484     {
485         // have no model
486         return Reference< XModel >();
487     }
488 
489 
getFrame()490     Reference< XFrame > SAL_CALL OPropertyBrowserController::getFrame(  )
491     {
492         return m_xFrame;
493     }
494 
495 
dispose()496     void SAL_CALL OPropertyBrowserController::dispose(  )
497     {
498         SolarMutexGuard aSolarGuard;
499 
500         // stop inspecting the current object
501         stopInspection( false );
502 
503         // say our dispose listeners goodbye
504         css::lang::EventObject aEvt;
505         aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
506         m_aDisposeListeners.disposeAndClear(aEvt);
507         m_aControlObservers.disposeAndClear(aEvt);
508 
509         // don't delete explicitly (this is done by the frame we reside in)
510         m_pView = nullptr;
511 
512         if ( m_xView.is() )
513             m_xView->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
514         m_xView.clear( );
515 
516         m_aInspectedObjects.clear();
517         impl_bindToNewModel_nothrow( nullptr );
518     }
519 
520 
addEventListener(const Reference<XEventListener> & _rxListener)521     void SAL_CALL OPropertyBrowserController::addEventListener( const Reference< XEventListener >& _rxListener )
522     {
523         m_aDisposeListeners.addInterface(_rxListener);
524     }
525 
526 
removeEventListener(const Reference<XEventListener> & _rxListener)527     void SAL_CALL OPropertyBrowserController::removeEventListener( const Reference< XEventListener >& _rxListener )
528     {
529         m_aDisposeListeners.removeInterface(_rxListener);
530     }
531 
532 
getImplementationName()533     OUString SAL_CALL OPropertyBrowserController::getImplementationName(  )
534     {
535         return getImplementationName_static();
536     }
537 
supportsService(const OUString & ServiceName)538     sal_Bool SAL_CALL OPropertyBrowserController::supportsService( const OUString& ServiceName )
539     {
540         return cppu::supportsService(this, ServiceName);
541     }
542 
543 
getSupportedServiceNames()544     Sequence< OUString > SAL_CALL OPropertyBrowserController::getSupportedServiceNames(  )
545     {
546         return getSupportedServiceNames_static();
547     }
548 
549 
getImplementationName_static()550     OUString OPropertyBrowserController::getImplementationName_static(  )
551     {
552         return "org.openoffice.comp.extensions.ObjectInspector";
553     }
554 
555 
getSupportedServiceNames_static()556     Sequence< OUString > OPropertyBrowserController::getSupportedServiceNames_static(  )
557     {
558         Sequence< OUString > aSupported { "com.sun.star.inspection.ObjectInspector" };
559         return aSupported;
560     }
561 
562 
Create(const Reference<XComponentContext> & _rxContext)563     Reference< XInterface > OPropertyBrowserController::Create(const Reference< XComponentContext >& _rxContext)
564     {
565         return *(new OPropertyBrowserController( _rxContext ) );
566     }
567 
568 
focusGained(const FocusEvent & _rSource)569     void SAL_CALL OPropertyBrowserController::focusGained( const FocusEvent& _rSource )
570     {
571         Reference< XWindow > xSourceWindow(_rSource.Source, UNO_QUERY);
572         Reference< XWindow > xContainerWindow;
573         if (m_xFrame.is())
574             xContainerWindow = m_xFrame->getContainerWindow();
575 
576         if ( xContainerWindow.get() == xSourceWindow.get() )
577         {   // our container window got the focus
578             if ( haveView() )
579                 getPropertyBox().GrabFocus();
580         }
581     }
582 
583 
focusLost(const FocusEvent &)584     void SAL_CALL OPropertyBrowserController::focusLost( const FocusEvent& /*_rSource*/ )
585     {
586         // not interested in
587     }
588 
589 
disposing(const EventObject & _rSource)590     void SAL_CALL OPropertyBrowserController::disposing( const EventObject& _rSource )
591     {
592         if ( m_xView.is() && ( m_xView == _rSource.Source ) )
593         {
594             m_xView = nullptr;
595             m_pView = nullptr;
596         }
597 
598         auto it = std::find_if(m_aInspectedObjects.begin(), m_aInspectedObjects.end(),
599             [&_rSource](const InterfaceArray::value_type& rxObj) { return rxObj == _rSource.Source; });
600         if (it != m_aInspectedObjects.end())
601             m_aInspectedObjects.erase(it);
602     }
603 
604 
IMPL_LINK_NOARG(OPropertyBrowserController,OnPageActivation,LinkParamNone *,void)605     IMPL_LINK_NOARG(OPropertyBrowserController, OnPageActivation, LinkParamNone*, void)
606     {
607         updateViewDataFromActivePage();
608     }
609 
610 
updateViewDataFromActivePage()611     void OPropertyBrowserController::updateViewDataFromActivePage()
612     {
613         if (!haveView())
614             return;
615 
616         OUString sOldSelection = m_sPageSelection;
617         m_sPageSelection.clear();
618 
619         const sal_uInt16 nCurrentPage = m_pView->getActivaPage();
620         if ( sal_uInt16(-1) != nCurrentPage )
621         {
622             for (auto const& pageId : m_aPageIds)
623             {
624                 if ( nCurrentPage == pageId.second )
625                 {
626                     m_sPageSelection = pageId.first;
627                     break;
628                 }
629             }
630         }
631 
632         if ( !m_sPageSelection.isEmpty() )
633             m_sLastValidPageSelection = m_sPageSelection;
634         else if ( !sOldSelection.isEmpty() )
635             m_sLastValidPageSelection = sOldSelection;
636     }
637 
638 
impl_getPageIdForCategory_nothrow(const OUString & _rCategoryName) const639     sal_uInt16 OPropertyBrowserController::impl_getPageIdForCategory_nothrow( const OUString& _rCategoryName ) const
640     {
641         sal_uInt16 nPageId = sal_uInt16(-1);
642         HashString2Int16::const_iterator pagePos = m_aPageIds.find( _rCategoryName );
643         if ( pagePos != m_aPageIds.end() )
644             nPageId = pagePos->second;
645         return nPageId;
646     }
647 
648 
selectPageFromViewData()649     void OPropertyBrowserController::selectPageFromViewData()
650     {
651         sal_uInt16 nNewPage = impl_getPageIdForCategory_nothrow( m_sPageSelection );
652 
653         if ( haveView() && ( nNewPage != sal_uInt16(-1) ) )
654             m_pView->activatePage( nNewPage );
655 
656         // just in case ...
657         updateViewDataFromActivePage();
658     }
659 
660 
Construct(vcl::Window * _pParentWin)661     void OPropertyBrowserController::Construct(vcl::Window* _pParentWin)
662     {
663         DBG_ASSERT(!haveView(), "OPropertyBrowserController::Construct: already have a view!");
664         DBG_ASSERT(_pParentWin, "OPropertyBrowserController::Construct: invalid parent window!");
665 
666         m_pView = VclPtr<OPropertyBrowserView>::Create(_pParentWin);
667         m_pView->setPageActivationHandler(LINK(this, OPropertyBrowserController, OnPageActivation));
668 
669         // add as dispose listener for our view. The view is disposed by the frame we're plugged into,
670         // and this disposal _deletes_ the view, so it would be deadly if we use our m_pView member
671         // after that
672         m_xView = VCLUnoHelper::GetInterface(m_pView);
673         if (m_xView.is())
674             m_xView->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
675 
676         getPropertyBox().SetLineListener(this);
677         getPropertyBox().SetControlObserver(this);
678         impl_initializeView_nothrow();
679 
680         m_pView->Show();
681     }
682 
683 
propertyChange(const PropertyChangeEvent & _rEvent)684     void SAL_CALL OPropertyBrowserController::propertyChange( const PropertyChangeEvent& _rEvent )
685     {
686         if ( _rEvent.Source == m_xModel )
687         {
688             if ( _rEvent.PropertyName == "IsReadOnly" )
689                // this is a huge cudgel, admitted.
690                 // The problem is that in case we were previously read-only, all our controls
691                 // were created read-only, too. We cannot simply switch them to not-read-only.
692                 // Even if they had an API for this, we do not know whether they were
693                 // originally created read-only, or if they are read-only just because
694                 // the model was.
695                 impl_rebindToInspectee_nothrow( m_aInspectedObjects );
696             return;
697         }
698 
699         if ( m_sCommittingProperty == _rEvent.PropertyName )
700             return;
701 
702         if ( !haveView() )
703             return;
704 
705         Any aNewValue( _rEvent.NewValue );
706         if ( impl_hasPropertyHandlerFor_nothrow( _rEvent.PropertyName ) )
707         {
708             // forward the new value to the property box, to reflect the change in the UI
709             aNewValue = impl_getPropertyValue_throw( _rEvent.PropertyName );
710 
711             // check whether the state is ambiguous. This is interesting in case we display the properties
712             // for multiple objects at once: In this case, we'll get a notification from one of the objects,
713             // but need to care for the "composed" value, which can be "ambiguous".
714             PropertyHandlerRef xHandler( impl_getHandlerForProperty_throw( _rEvent.PropertyName ), UNO_SET_THROW );
715             PropertyState ePropertyState( xHandler->getPropertyState( _rEvent.PropertyName ) );
716             bool bAmbiguousValue = ( PropertyState_AMBIGUOUS_VALUE == ePropertyState );
717 
718             getPropertyBox().SetPropertyValue( _rEvent.PropertyName, aNewValue, bAmbiguousValue );
719         }
720 
721         // if it's an actuating property, then update the UI for any dependent
722         // properties
723         if ( impl_isActuatingProperty_nothrow( _rEvent.PropertyName ) )
724             impl_broadcastPropertyChange_nothrow( _rEvent.PropertyName, aNewValue, _rEvent.OldValue, false );
725     }
726 
727 
createPropertyControl(::sal_Int16 ControlType,sal_Bool CreateReadOnly)728     Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::createPropertyControl( ::sal_Int16 ControlType, sal_Bool CreateReadOnly )
729     {
730         ::osl::MutexGuard aGuard( m_aMutex );
731 
732         Reference< XPropertyControl > xControl;
733 
734         // default winbits: a border only
735         WinBits nWinBits = WB_BORDER;
736 
737         // read-only-ness
738         CreateReadOnly |= impl_isReadOnlyModel_throw() ? 1 : 0;
739         if ( CreateReadOnly )
740             nWinBits |= WB_READONLY;
741 
742         switch ( ControlType )
743         {
744             case PropertyControlType::StringListField:
745                 xControl = new OMultilineEditControl( &getPropertyBox(), eStringList, nWinBits | WB_DROPDOWN | WB_TABSTOP );
746                 break;
747 
748             case PropertyControlType::MultiLineTextField:
749                 xControl = new OMultilineEditControl( &getPropertyBox(), eMultiLineText, nWinBits | WB_DROPDOWN | WB_TABSTOP );
750                 break;
751 
752             case PropertyControlType::ListBox:
753                 xControl = new OListboxControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN);
754                 break;
755 
756             case PropertyControlType::ComboBox:
757                 xControl = new OComboboxControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN);
758                 break;
759 
760             case PropertyControlType::TextField:
761                 xControl = new OEditControl( &getPropertyBox(), false, nWinBits | WB_TABSTOP );
762                 break;
763 
764             case PropertyControlType::CharacterField:
765                 xControl = new OEditControl( &getPropertyBox(), true, nWinBits | WB_TABSTOP );
766                 break;
767 
768             case PropertyControlType::NumericField:
769                 xControl = new ONumericControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_SPIN | WB_REPEAT );
770                 break;
771 
772             case PropertyControlType::DateTimeField:
773                 xControl = new ODateTimeControl( &getPropertyBox(), nWinBits | WB_TABSTOP );
774                 break;
775 
776             case PropertyControlType::DateField:
777                 xControl = new ODateControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_SPIN | WB_REPEAT );
778                 break;
779 
780             case PropertyControlType::TimeField:
781                 xControl = new OTimeControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_SPIN | WB_REPEAT );
782                 break;
783 
784             case PropertyControlType::ColorListBox:
785                 xControl = new OColorControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN );
786                 break;
787 
788             case PropertyControlType::HyperlinkField:
789                 xControl = new OHyperlinkControl( &getPropertyBox(), nWinBits | WB_TABSTOP | WB_DROPDOWN );
790                 break;
791 
792             default:
793                 throw IllegalArgumentException( OUString(), *this, 1 );
794         }
795 
796         return xControl;
797     }
798 
799 
impl_toggleInspecteeListening_nothrow(bool _bOn)800     void OPropertyBrowserController::impl_toggleInspecteeListening_nothrow( bool _bOn )
801     {
802         for (auto const& inspectedObject : m_aInspectedObjects)
803         {
804             try
805             {
806                 Reference< XComponent > xComp( inspectedObject, UNO_QUERY );
807                 if ( xComp.is() )
808                 {
809                     if ( _bOn )
810                         xComp->addEventListener( static_cast< XPropertyChangeListener* >( this ) );
811                     else
812                         xComp->removeEventListener( static_cast< XPropertyChangeListener* >( this ) );
813                 }
814             }
815             catch( const Exception& )
816             {
817                 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
818             }
819         }
820     }
821 
822 
stopInspection(bool _bCommitModified)823     void OPropertyBrowserController::stopInspection( bool _bCommitModified )
824     {
825         if ( haveView() )
826         {
827             if ( _bCommitModified )
828                 // commit the editor's content
829                 getPropertyBox().CommitModified();
830 
831             // hide the property box so that it does not flicker
832             getPropertyBox().Hide();
833 
834             // clear the property box
835             getPropertyBox().ClearAll();
836         }
837 
838         // destroy the view first
839         if ( haveView() )
840         {
841             // remove the pages
842             for (auto const& pageId : m_aPageIds)
843                 getPropertyBox().RemovePage( pageId.second );
844             clearContainer( m_aPageIds );
845         }
846 
847         clearContainer( m_aProperties );
848 
849         // de-register as dispose-listener from our inspected objects
850         impl_toggleInspecteeListening_nothrow( false );
851 
852         // handlers are obsolete, so is our "composer" for their UI requests
853         if (m_pUIRequestComposer)
854             m_pUIRequestComposer->dispose();
855         m_pUIRequestComposer.reset();
856 
857         // clean up the property handlers
858         PropertyHandlerArray aAllHandlers;  // will contain every handler exactly once
859         for (auto const& propertyHandler : m_aPropertyHandlers)
860             if ( std::find( aAllHandlers.begin(), aAllHandlers.end(), propertyHandler.second ) == aAllHandlers.end() )
861                 aAllHandlers.push_back( propertyHandler.second );
862 
863         for (auto const& handler : aAllHandlers)
864         {
865             try
866             {
867                 handler->removePropertyChangeListener( this );
868                 handler->dispose();
869             }
870             catch( const DisposedException& )
871             {
872             }
873             catch( const Exception& )
874             {
875                 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
876             }
877         }
878 
879         clearContainer( m_aPropertyHandlers );
880         clearContainer( m_aDependencyHandlers );
881     }
882 
883 
impl_hasPropertyHandlerFor_nothrow(const OUString & _rPropertyName) const884     bool OPropertyBrowserController::impl_hasPropertyHandlerFor_nothrow( const OUString& _rPropertyName ) const
885     {
886         PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
887         return ( handlerPos != m_aPropertyHandlers.end() );
888     }
889 
890 
impl_getHandlerForProperty_throw(const OUString & _rPropertyName) const891     OPropertyBrowserController::PropertyHandlerRef const & OPropertyBrowserController::impl_getHandlerForProperty_throw( const OUString& _rPropertyName ) const
892     {
893         PropertyHandlerRepository::const_iterator handlerPos = m_aPropertyHandlers.find( _rPropertyName );
894         if ( handlerPos == m_aPropertyHandlers.end() )
895             throw RuntimeException();
896         return handlerPos->second;
897     }
898 
899 
impl_getPropertyValue_throw(const OUString & _rPropertyName)900     Any OPropertyBrowserController::impl_getPropertyValue_throw( const OUString& _rPropertyName )
901     {
902         PropertyHandlerRef handler = impl_getHandlerForProperty_throw( _rPropertyName );
903         return handler->getPropertyValue( _rPropertyName );
904     }
905 
906 
impl_rebindToInspectee_nothrow(const InterfaceArray & _rObjects)907     void OPropertyBrowserController::impl_rebindToInspectee_nothrow( const InterfaceArray& _rObjects )
908     {
909         try
910         {
911             // stop inspecting the old object(s)
912             stopInspection( true );
913 
914             // inspect the new object(s)
915             m_aInspectedObjects = _rObjects;
916             doInspection();
917 
918             // update the user interface
919             UpdateUI();
920         }
921 
922         catch(const Exception&)
923         {
924             OSL_FAIL("OPropertyBrowserController::impl_rebindToInspectee_nothrow: caught an exception !");
925         }
926     }
927 
928 
doInspection()929     void OPropertyBrowserController::doInspection()
930     {
931         try
932         {
933 
934             // obtain the properties of the object
935             std::vector< Property > aProperties;
936 
937             PropertyHandlerArray aPropertyHandlers;
938             getPropertyHandlers( m_aInspectedObjects, aPropertyHandlers );
939 
940             PropertyHandlerArray::iterator aHandler( aPropertyHandlers.begin() );
941             while ( aHandler != aPropertyHandlers.end() )
942             {
943                 DBG_ASSERT( aHandler->get(), "OPropertyBrowserController::doInspection: invalid handler!" );
944 
945                 StlSyntaxSequence< Property > aThisHandlersProperties(  (*aHandler)->getSupportedProperties() );
946 
947                 if ( aThisHandlersProperties.empty() )
948                 {
949                     // this handler doesn't know anything about the current inspectee -> ignore it
950                     (*aHandler)->dispose();
951                     aHandler = aPropertyHandlers.erase( aHandler );
952                     continue;
953                 }
954 
955                 // append these properties to our "all properties" array
956                 aProperties.reserve( aProperties.size() + aThisHandlersProperties.size() );
957                 for (const auto & aThisHandlersPropertie : aThisHandlersProperties)
958                 {
959                     auto noPrevious = std::none_of(
960                         aProperties.begin(),
961                         aProperties.end(),
962                         FindPropertyByName( aThisHandlersPropertie.Name )
963                     );
964                     if ( noPrevious )
965                     {
966                         aProperties.push_back( aThisHandlersPropertie );
967                         continue;
968                     }
969 
970                     // there already was another (previous) handler which supported this property.
971                     // Don't add it to aProperties, again.
972 
973                     // Also, ensure that handlers which previously expressed interest in *changes*
974                     // of this property are not notified.
975                     // This is 'cause we have a new handler which is responsible for this property,
976                     // which means it can give it a completely different meaning than the previous
977                     // handler for this property is prepared for.
978                     std::pair< PropertyHandlerMultiRepository::iterator, PropertyHandlerMultiRepository::iterator >
979                         aDepHandlers = m_aDependencyHandlers.equal_range( aThisHandlersPropertie.Name );
980                     m_aDependencyHandlers.erase( aDepHandlers.first, aDepHandlers.second );
981                 }
982 
983                 // determine the superseded properties
984                 StlSyntaxSequence< OUString > aSupersededByThisHandler( (*aHandler)->getSupersededProperties() );
985                 for (const auto & superseded : aSupersededByThisHandler)
986                 {
987                     std::vector< Property >::iterator existent = std::find_if(
988                         aProperties.begin(),
989                         aProperties.end(),
990                         FindPropertyByName( superseded )
991                     );
992                     if ( existent != aProperties.end() )
993                         // one of the properties superseded by this handler was supported by a previous
994                         // one -> erase
995                         aProperties.erase( existent );
996                 }
997 
998                 // be notified of changes which this handler is responsible for
999                 (*aHandler)->addPropertyChangeListener( this );
1000 
1001                 // remember this handler for every of the properties which it is responsible
1002                 // for
1003                 for (const auto & aThisHandlersPropertie : aThisHandlersProperties)
1004                 {
1005                     m_aPropertyHandlers[ aThisHandlersPropertie.Name ] = *aHandler;
1006                     // note that this implies that if two handlers support the same property,
1007                     // the latter wins
1008                 }
1009 
1010                 // see if the handler expresses interest in any actuating properties
1011                 StlSyntaxSequence< OUString > aInterestingActuations( (*aHandler)->getActuatingProperties() );
1012                 for (const auto & aInterestingActuation : aInterestingActuations)
1013                 {
1014                     m_aDependencyHandlers.emplace( aInterestingActuation, *aHandler );
1015                 }
1016 
1017                 ++aHandler;
1018             }
1019 
1020             // create a new composer for UI requests coming from the handlers
1021             m_pUIRequestComposer.reset( new ComposedPropertyUIUpdate( getInspectorUI(), this ) );
1022 
1023             // sort the properties by relative position, as indicated by the model
1024             sal_Int32 nPos = 0;
1025             for (auto const& sourceProps : aProperties)
1026             {
1027                 sal_Int32 nRelativePropertyOrder = nPos;
1028                 if ( m_xModel.is() )
1029                     nRelativePropertyOrder = m_xModel->getPropertyOrderIndex( sourceProps.Name );
1030                 m_aProperties.emplace(nRelativePropertyOrder, sourceProps);
1031                 ++nPos;
1032             }
1033 
1034             // be notified when one of our inspectees dies
1035             impl_toggleInspecteeListening_nothrow( true );
1036         }
1037         catch(const Exception&)
1038         {
1039             OSL_FAIL("OPropertyBrowserController::doInspection : caught an exception !");
1040         }
1041     }
1042 
1043 
getMinimumSize()1044     css::awt::Size SAL_CALL OPropertyBrowserController::getMinimumSize()
1045     {
1046         css::awt::Size aSize;
1047         if( m_pView )
1048             return m_pView->getMinimumSize();
1049         else
1050             return aSize;
1051     }
1052 
1053 
getPreferredSize()1054     css::awt::Size SAL_CALL OPropertyBrowserController::getPreferredSize()
1055     {
1056         return getMinimumSize();
1057     }
1058 
1059 
calcAdjustedSize(const css::awt::Size & _rNewSize)1060     css::awt::Size SAL_CALL OPropertyBrowserController::calcAdjustedSize( const css::awt::Size& _rNewSize )
1061     {
1062         awt::Size aMinSize = getMinimumSize( );
1063         awt::Size aAdjustedSize( _rNewSize );
1064         if ( aAdjustedSize.Width < aMinSize.Width )
1065             aAdjustedSize.Width = aMinSize.Width;
1066         if ( aAdjustedSize.Height < aMinSize.Height )
1067             aAdjustedSize.Height = aMinSize.Height;
1068         return aAdjustedSize;
1069     }
1070 
1071 
describePropertyLine(const Property & _rProperty,OLineDescriptor & _rDescriptor)1072     void OPropertyBrowserController::describePropertyLine( const Property& _rProperty, OLineDescriptor& _rDescriptor )
1073     {
1074         try
1075         {
1076             PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rProperty.Name );
1077             if ( handler == m_aPropertyHandlers.end() )
1078                 throw RuntimeException();   // caught below
1079 
1080             _rDescriptor.assignFrom( handler->second->describePropertyLine( _rProperty.Name, this ) );
1081 
1082 
1083             _rDescriptor.xPropertyHandler = handler->second;
1084             _rDescriptor.sName = _rProperty.Name;
1085             _rDescriptor.aValue = _rDescriptor.xPropertyHandler->getPropertyValue( _rProperty.Name );
1086 
1087             if ( _rDescriptor.DisplayName.isEmpty() )
1088             {
1089             #ifdef DBG_UTIL
1090                 SAL_WARN( "extensions.propctrlr", "OPropertyBrowserController::describePropertyLine: handler did not provide a display name for '"
1091                         <<_rProperty.Name << "'!" );
1092             #endif
1093                 _rDescriptor.DisplayName = _rProperty.Name;
1094             }
1095 
1096             PropertyState   ePropertyState( _rDescriptor.xPropertyHandler->getPropertyState( _rProperty.Name ) );
1097             if ( PropertyState_AMBIGUOUS_VALUE == ePropertyState )
1098             {
1099                 _rDescriptor.bUnknownValue = true;
1100                 _rDescriptor.aValue.clear();
1101             }
1102 
1103             _rDescriptor.bReadOnly = impl_isReadOnlyModel_throw();
1104         }
1105         catch( const Exception& )
1106         {
1107             OSL_FAIL( "OPropertyBrowserController::describePropertyLine: caught an exception!" );
1108         }
1109     }
1110 
1111 
impl_buildCategories_throw()1112     void OPropertyBrowserController::impl_buildCategories_throw()
1113     {
1114         OSL_PRECOND( m_aPageIds.empty(), "OPropertyBrowserController::impl_buildCategories_throw: duplicate call!" );
1115 
1116         StlSyntaxSequence< PropertyCategoryDescriptor > aCategories;
1117         if ( m_xModel.is() )
1118             aCategories = StlSyntaxSequence< PropertyCategoryDescriptor >(m_xModel->describeCategories());
1119 
1120         for (auto const& category : aCategories)
1121         {
1122             OSL_ENSURE( m_aPageIds.find( category.ProgrammaticName ) == m_aPageIds.end(),
1123                 "OPropertyBrowserController::impl_buildCategories_throw: duplicate programmatic name!" );
1124 
1125             m_aPageIds[ category.ProgrammaticName ] =
1126                 getPropertyBox().AppendPage( category.UIName, HelpIdUrl::getHelpId( category.HelpURL ) );
1127         }
1128     }
1129 
1130 
UpdateUI()1131     void OPropertyBrowserController::UpdateUI()
1132     {
1133         try
1134         {
1135             if ( !haveView() )
1136                 // too early, will return later
1137                 return;
1138 
1139             getPropertyBox().DisableUpdate();
1140 
1141             bool bHaveFocus = getPropertyBox().HasChildPathFocus();
1142 
1143             // create our tab pages
1144             impl_buildCategories_throw();
1145             // (and allow for pages to be actually unused)
1146             std::set< sal_uInt16 > aUsedPages;
1147 
1148             // when building the UI below, remember which properties are actuating,
1149             // to allow for an initial actuatingPropertyChanged call
1150             std::vector< OUString > aActuatingProperties;
1151             std::vector< Any > aActuatingPropertyValues;
1152 
1153             // ask the handlers to describe the property UI, and insert the resulting
1154             // entries into our list boxes
1155             for (auto const& property : m_aProperties)
1156             {
1157                 OLineDescriptor aDescriptor;
1158                 describePropertyLine( property.second, aDescriptor );
1159 
1160                 bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( property.second.Name );
1161 
1162                 SAL_WARN_IF( aDescriptor.Category.isEmpty(), "extensions.propctrlr",
1163                         "OPropertyBrowserController::UpdateUI: empty category provided for property '"
1164                         << property.second.Name << "'!");
1165                 // finally insert this property control
1166                 sal_uInt16 nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
1167                 if ( nTargetPageId == sal_uInt16(-1) )
1168                 {
1169                     // this category does not yet exist. This is allowed, as an inspector model might be lazy, and not provide
1170                     // any category information of its own. In this case, we have a fallback ...
1171                     m_aPageIds[ aDescriptor.Category ] =
1172                         getPropertyBox().AppendPage( aDescriptor.Category, OString() );
1173                     nTargetPageId = impl_getPageIdForCategory_nothrow( aDescriptor.Category );
1174                 }
1175 
1176                 getPropertyBox().InsertEntry( aDescriptor, nTargetPageId );
1177                 aUsedPages.insert( nTargetPageId );
1178 
1179                 // if it's an actuating property, remember it
1180                 if ( bIsActuatingProperty )
1181                 {
1182                     aActuatingProperties.push_back( property.second.Name );
1183                     aActuatingPropertyValues.push_back( impl_getPropertyValue_throw( property.second.Name ) );
1184                 }
1185             }
1186 
1187             // update any dependencies for the actuating properties which we encountered
1188             {
1189                 std::vector< Any >::const_iterator aPropertyValue = aActuatingPropertyValues.begin();
1190                 for (auto const& actuatingProperty : aActuatingProperties)
1191                 {
1192                     impl_broadcastPropertyChange_nothrow( actuatingProperty, *aPropertyValue, *aPropertyValue, true );
1193                     ++aPropertyValue;
1194                 }
1195             }
1196 
1197             // remove any unused pages (which we did not encounter properties for)
1198             HashString2Int16 aSurvivingPageIds;
1199             for (auto const& pageId : m_aPageIds)
1200             {
1201                 if ( aUsedPages.find( pageId.second ) == aUsedPages.end() )
1202                     getPropertyBox().RemovePage( pageId.second );
1203                 else
1204                     aSurvivingPageIds.insert(pageId);
1205             }
1206             m_aPageIds.swap( aSurvivingPageIds );
1207 
1208 
1209             getPropertyBox().Show();
1210             getPropertyBox().EnableUpdate();
1211             if ( bHaveFocus )
1212                 getPropertyBox().GrabFocus();
1213 
1214             // activate the first page
1215             if ( !m_aPageIds.empty() )
1216             {
1217                 Sequence< PropertyCategoryDescriptor > aCategories( m_xModel->describeCategories() );
1218                 if ( aCategories.hasElements() )
1219                     m_pView->activatePage( m_aPageIds[ aCategories[0].ProgrammaticName ] );
1220                 else
1221                     // allowed: if we default-created the pages ...
1222                     m_pView->activatePage( m_aPageIds.begin()->second );
1223             }
1224 
1225             // activate the previously active page (if possible)
1226             if ( !m_sLastValidPageSelection.isEmpty() )
1227                 m_sPageSelection = m_sLastValidPageSelection;
1228             selectPageFromViewData();
1229         }
1230         catch( const Exception& )
1231         {
1232             DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1233         }
1234     }
1235 
1236 
Clicked(const OUString & _rName,bool _bPrimary)1237     void OPropertyBrowserController::Clicked( const OUString& _rName, bool _bPrimary )
1238     {
1239         try
1240         {
1241             // since the browse buttons do not get the focus when clicked with the mouse,
1242             // we need to commit the changes in the current property field
1243             getPropertyBox().CommitModified();
1244 
1245             PropertyHandlerRepository::const_iterator handler = m_aPropertyHandlers.find( _rName );
1246             DBG_ASSERT( handler != m_aPropertyHandlers.end(), "OPropertyBrowserController::Clicked: a property without handler? This will crash!" );
1247 
1248             ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
1249 
1250             Any aData;
1251             m_xInteractiveHandler = handler->second;
1252             InteractiveSelectionResult eResult =
1253                 handler->second->onInteractivePropertySelection( _rName, _bPrimary, aData,
1254                     m_pUIRequestComposer->getUIForPropertyHandler( handler->second ) );
1255 
1256             switch ( eResult )
1257             {
1258             case InteractiveSelectionResult_Cancelled:
1259             case InteractiveSelectionResult_Success:
1260                 // okay, nothing to do
1261                 break;
1262             case InteractiveSelectionResult_ObtainedValue:
1263                 handler->second->setPropertyValue( _rName, aData );
1264                 break;
1265             case InteractiveSelectionResult_Pending:
1266                 // also okay, we expect that the handler has disabled the UI as necessary
1267                 break;
1268             default:
1269                 OSL_FAIL( "OPropertyBrowserController::Clicked: unknown result value!" );
1270                 break;
1271             }
1272         }
1273         catch (const Exception&)
1274         {
1275             DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1276         }
1277         m_xInteractiveHandler = nullptr;
1278     }
1279 
1280 
hasPropertyByName(const OUString & _rName)1281     bool OPropertyBrowserController::hasPropertyByName( const OUString& _rName )
1282     {
1283         for (auto const& property : m_aProperties)
1284             if ( property.second.Name == _rName )
1285                 return true;
1286         return false;
1287     }
1288 
1289 
Commit(const OUString & rName,const Any & _rValue)1290     void OPropertyBrowserController::Commit( const OUString& rName, const Any& _rValue )
1291     {
1292         try
1293         {
1294             OUString sPlcHolder = PcrRes(RID_EMBED_IMAGE_PLACEHOLDER);
1295             bool bIsPlaceHolderValue = false;
1296 
1297             if ( rName == PROPERTY_IMAGE_URL )
1298             {
1299                 // if the prop value is the PlaceHolder
1300                 // can ignore it
1301                 OUString sVal;
1302                 _rValue >>= sVal;
1303                 if ( sVal == sPlcHolder )
1304                     bIsPlaceHolderValue = true;
1305             }
1306             m_sCommittingProperty = rName;
1307 
1308             bool bIsActuatingProperty = impl_isActuatingProperty_nothrow( rName );
1309 
1310             Any aOldValue;
1311             if ( bIsActuatingProperty )
1312                 aOldValue = impl_getPropertyValue_throw( rName );
1313 
1314             // do we have a dedicated handler for this property, which we can delegate some tasks to?
1315             PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
1316 
1317 
1318             // set the value ( only if it's not a placeholder )
1319             if ( !bIsPlaceHolderValue )
1320                 handler->setPropertyValue( rName, _rValue );
1321 
1322 
1323             // re-retrieve the value
1324             Any aNormalizedValue = handler->getPropertyValue( rName );
1325 
1326             // care for any inter-property dependencies
1327             if ( bIsActuatingProperty )
1328                 impl_broadcastPropertyChange_nothrow( rName, aNormalizedValue, aOldValue, false );
1329 
1330             // and display it again. This ensures proper formatting
1331             getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
1332         }
1333         catch(const PropertyVetoException& eVetoException)
1334         {
1335             std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_pView ? m_pView->GetFrameWeld() : nullptr,
1336                                                           VclMessageType::Info, VclButtonsType::Ok,
1337                                                           eVetoException.Message));
1338             xInfoBox->run();
1339             PropertyHandlerRef handler = impl_getHandlerForProperty_throw( rName );
1340             Any aNormalizedValue = handler->getPropertyValue( rName );
1341             getPropertyBox().SetPropertyValue( rName, aNormalizedValue, false );
1342         }
1343         catch(const Exception&)
1344         {
1345             OSL_FAIL("OPropertyBrowserController::Commit : caught an exception !");
1346         }
1347 
1348         m_sCommittingProperty.clear();
1349     }
1350 
1351 
focusGained(const Reference<XPropertyControl> & Control)1352     void OPropertyBrowserController::focusGained( const Reference< XPropertyControl >& Control )
1353     {
1354         m_aControlObservers.notifyEach( &XPropertyControlObserver::focusGained, Control );
1355     }
1356 
1357 
valueChanged(const Reference<XPropertyControl> & Control)1358     void OPropertyBrowserController::valueChanged( const Reference< XPropertyControl >& Control )
1359     {
1360         m_aControlObservers.notifyEach( &XPropertyControlObserver::valueChanged, Control );
1361     }
1362 
1363 
1364     namespace
1365     {
lcl_createHandler(const Reference<XComponentContext> & _rContext,const Any & _rFactoryDescriptor)1366         Reference< XPropertyHandler > lcl_createHandler( const Reference<XComponentContext>& _rContext, const Any& _rFactoryDescriptor )
1367         {
1368             Reference< XPropertyHandler > xHandler;
1369 
1370             OUString sServiceName;
1371             Reference< XSingleServiceFactory > xServiceFac;
1372             Reference< XSingleComponentFactory > xComponentFac;
1373 
1374             if ( _rFactoryDescriptor >>= sServiceName )
1375                 xHandler.set( _rContext->getServiceManager()->createInstanceWithContext( sServiceName, _rContext ), UNO_QUERY );
1376             else if ( _rFactoryDescriptor >>= xServiceFac )
1377                 xHandler.set(xServiceFac->createInstance(), css::uno::UNO_QUERY);
1378             else if ( _rFactoryDescriptor >>= xComponentFac )
1379                 xHandler.set(xComponentFac->createInstanceWithContext( _rContext ), css::uno::UNO_QUERY);
1380             OSL_ENSURE(xHandler.is(),"lcl_createHandler: Can not create handler");
1381             return xHandler;
1382         }
1383     }
1384 
1385 
getPropertyHandlers(const InterfaceArray & _rObjects,PropertyHandlerArray & _rHandlers)1386     void OPropertyBrowserController::getPropertyHandlers( const InterfaceArray& _rObjects, PropertyHandlerArray& _rHandlers )
1387     {
1388         _rHandlers.resize( 0 );
1389         if ( _rObjects.empty() )
1390             return;
1391 
1392         // create a component context for the handlers, containing some information about where
1393         // they live
1394         Reference< XComponentContext > xHandlerContext( m_xContext );
1395 
1396         // if our own creator did not pass a dialog parent window, use our own view for this
1397         Reference< XWindow > xParentWindow;
1398         Any any = m_xContext->getValueByName( "DialogParentWindow" );
1399         any >>= xParentWindow;
1400         if ( !xParentWindow.is() )
1401         {
1402             ::cppu::ContextEntry_Init aHandlerContextInfo[] =
1403             {
1404                 ::cppu::ContextEntry_Init( "DialogParentWindow", makeAny( VCLUnoHelper::GetInterface( m_pView ) ) )
1405             };
1406             xHandlerContext = ::cppu::createComponentContext(
1407                 aHandlerContextInfo, SAL_N_ELEMENTS( aHandlerContextInfo ),
1408                 m_xContext );
1409         }
1410 
1411         Sequence< Any > aHandlerFactories;
1412         if ( m_xModel.is() )
1413             aHandlerFactories = m_xModel->getHandlerFactories();
1414 
1415         for ( auto const & handlerFactory : std::as_const(aHandlerFactories) )
1416         {
1417             if ( _rObjects.size() == 1 )
1418             {   // we're inspecting only one object -> one handler
1419                 Reference< XPropertyHandler > xHandler( lcl_createHandler( m_xContext, handlerFactory ) );
1420                 if ( xHandler.is() )
1421                 {
1422                     xHandler->inspect( _rObjects[0] );
1423                     _rHandlers.push_back( xHandler );
1424                 }
1425             }
1426             else
1427             {
1428                 // create a single handler for every single object
1429                 std::vector< Reference< XPropertyHandler > > aSingleHandlers( _rObjects.size() );
1430                 std::vector< Reference< XPropertyHandler > >::iterator pHandler = aSingleHandlers.begin();
1431 
1432                 for (auto const& elem : _rObjects)
1433                 {
1434                     *pHandler = lcl_createHandler( m_xContext, handlerFactory );
1435                     if ( pHandler->is() )
1436                     {
1437                         (*pHandler)->inspect(elem);
1438                         ++pHandler;
1439                     }
1440                 }
1441                 aSingleHandlers.resize( pHandler - aSingleHandlers.begin() );
1442 
1443                 // then create a handler which composes information out of those single handlers
1444                 if ( !aSingleHandlers.empty() )
1445                     _rHandlers.push_back( new PropertyComposer( aSingleHandlers ) );
1446             }
1447         }
1448 
1449         // note that the handlers will not be used by our caller, if they indicate that there are no
1450         // properties they feel responsible for
1451     }
1452 
1453 
impl_findObjectProperty_nothrow(const OUString & _rName,OrderedPropertyMap::const_iterator * _pProperty)1454     bool OPropertyBrowserController::impl_findObjectProperty_nothrow( const OUString& _rName, OrderedPropertyMap::const_iterator* _pProperty )
1455     {
1456         OrderedPropertyMap::const_iterator search = std::find_if(m_aProperties.begin(), m_aProperties.end(),
1457             [&_rName](const OrderedPropertyMap::value_type& rEntry) { return rEntry.second.Name == _rName; });
1458         if ( _pProperty )
1459             *_pProperty = search;
1460         return ( search != m_aProperties.end() );
1461     }
1462 
1463 
rebuildPropertyUI(const OUString & _rPropertyName)1464     void OPropertyBrowserController::rebuildPropertyUI( const OUString& _rPropertyName )
1465     {
1466         ::osl::MutexGuard aGuard( m_aMutex );
1467         if ( !haveView() )
1468             throw RuntimeException();
1469 
1470         OrderedPropertyMap::const_iterator propertyPos;
1471         if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1472             return;
1473 
1474         OLineDescriptor aDescriptor;
1475         try
1476         {
1477             describePropertyLine( propertyPos->second, aDescriptor );
1478         }
1479         catch( const Exception& )
1480         {
1481             OSL_FAIL( "OPropertyBrowserController::rebuildPropertyUI: caught an exception!" );
1482         }
1483 
1484         getPropertyBox().ChangeEntry( aDescriptor );
1485    }
1486 
1487 
enablePropertyUI(const OUString & _rPropertyName,sal_Bool _bEnable)1488     void OPropertyBrowserController::enablePropertyUI( const OUString& _rPropertyName, sal_Bool _bEnable )
1489     {
1490         ::osl::MutexGuard aGuard( m_aMutex );
1491         if ( !haveView() )
1492             throw RuntimeException();
1493 
1494         if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1495             return;
1496 
1497         getPropertyBox().EnablePropertyLine( _rPropertyName, _bEnable );
1498     }
1499 
1500 
enablePropertyUIElements(const OUString & _rPropertyName,sal_Int16 _nElements,sal_Bool _bEnable)1501     void OPropertyBrowserController::enablePropertyUIElements( const OUString& _rPropertyName, sal_Int16 _nElements, sal_Bool _bEnable )
1502     {
1503         ::osl::MutexGuard aGuard( m_aMutex );
1504         if ( !haveView() )
1505             throw RuntimeException();
1506 
1507         if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1508             return;
1509 
1510         getPropertyBox().EnablePropertyControls( _rPropertyName, _nElements, _bEnable );
1511     }
1512 
1513 
showPropertyUI(const OUString & _rPropertyName)1514     void OPropertyBrowserController::showPropertyUI( const OUString& _rPropertyName )
1515     {
1516         ::osl::MutexGuard aGuard( m_aMutex );
1517         if ( !haveView() )
1518             throw RuntimeException();
1519 
1520         // look up the property in our object properties
1521         OrderedPropertyMap::const_iterator propertyPos;
1522         if ( !impl_findObjectProperty_nothrow( _rPropertyName, &propertyPos ) )
1523             return;
1524 
1525         if ( getPropertyBox().GetPropertyPos( _rPropertyName ) != EDITOR_LIST_ENTRY_NOTFOUND )
1526         {
1527             rebuildPropertyUI( _rPropertyName );
1528             return;
1529         }
1530 
1531         OLineDescriptor aDescriptor;
1532         describePropertyLine( propertyPos->second, aDescriptor );
1533 
1534         // look for the position to insert the property
1535 
1536         // side note: The methods GetPropertyPos and InsertEntry of the OPropertyEditor work
1537         // only on the current page. This implies that it's impossible to use this method here
1538         // to show property lines which are *not* on the current page.
1539         // This is sufficient for now, but should be changed in the future.
1540 
1541         // by definition, the properties in m_aProperties are in the order in which they appear in the UI
1542         // So all we need is a predecessor of pProperty in m_aProperties
1543         sal_uInt16 nUIPos = EDITOR_LIST_ENTRY_NOTFOUND;
1544         do
1545         {
1546             if ( propertyPos != m_aProperties.begin() )
1547                 --propertyPos;
1548             nUIPos = getPropertyBox().GetPropertyPos( propertyPos->second.Name );
1549         }
1550         while ( ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND ) && ( propertyPos != m_aProperties.begin() ) );
1551 
1552         if ( nUIPos == EDITOR_LIST_ENTRY_NOTFOUND )
1553             // insert at the very top
1554             nUIPos = 0;
1555         else
1556             // insert right after the predecessor we found
1557             ++nUIPos;
1558 
1559         getPropertyBox().InsertEntry(
1560             aDescriptor, impl_getPageIdForCategory_nothrow( aDescriptor.Category ), nUIPos );
1561     }
1562 
1563 
hidePropertyUI(const OUString & _rPropertyName)1564     void OPropertyBrowserController::hidePropertyUI( const OUString& _rPropertyName )
1565     {
1566         ::osl::MutexGuard aGuard( m_aMutex );
1567         if ( !haveView() )
1568             throw RuntimeException();
1569 
1570         if ( !impl_findObjectProperty_nothrow( _rPropertyName ) )
1571             return;
1572 
1573         getPropertyBox().RemoveEntry( _rPropertyName );
1574     }
1575 
1576 
showCategory(const OUString & _rCategory,sal_Bool _bShow)1577     void OPropertyBrowserController::showCategory( const OUString& _rCategory, sal_Bool _bShow )
1578     {
1579         ::osl::MutexGuard aGuard( m_aMutex );
1580         if ( !haveView() )
1581             throw RuntimeException();
1582 
1583         sal_uInt16 nPageId = impl_getPageIdForCategory_nothrow( _rCategory );
1584         OSL_ENSURE( nPageId != sal_uInt16(-1), "OPropertyBrowserController::showCategory: invalid category!" );
1585 
1586         getPropertyBox().ShowPropertyPage( nPageId, _bShow );
1587     }
1588 
1589 
getPropertyControl(const OUString & _rPropertyName)1590     Reference< XPropertyControl > SAL_CALL OPropertyBrowserController::getPropertyControl( const OUString& _rPropertyName )
1591     {
1592         ::osl::MutexGuard aGuard( m_aMutex );
1593         if ( !haveView() )
1594             throw RuntimeException();
1595 
1596         Reference< XPropertyControl > xControl( getPropertyBox().GetPropertyControl( _rPropertyName ) );
1597         return xControl;
1598     }
1599 
1600 
registerControlObserver(const Reference<XPropertyControlObserver> & Observer)1601     void SAL_CALL OPropertyBrowserController::registerControlObserver( const Reference< XPropertyControlObserver >& Observer )
1602     {
1603         m_aControlObservers.addInterface( Observer );
1604     }
1605 
1606 
revokeControlObserver(const Reference<XPropertyControlObserver> & Observer)1607     void SAL_CALL OPropertyBrowserController::revokeControlObserver( const Reference< XPropertyControlObserver >& Observer )
1608     {
1609         m_aControlObservers.removeInterface( Observer );
1610     }
1611 
1612 
setHelpSectionText(const OUString & _rHelpText)1613     void SAL_CALL OPropertyBrowserController::setHelpSectionText( const OUString& _rHelpText )
1614     {
1615         SolarMutexGuard aSolarGuard;
1616         ::osl::MutexGuard aGuard( m_aMutex );
1617 
1618         if ( !haveView() )
1619             throw DisposedException();
1620 
1621         if ( !getPropertyBox().HasHelpSection() )
1622             throw NoSupportException();
1623 
1624         getPropertyBox().SetHelpText( _rHelpText );
1625     }
1626 
1627 
impl_broadcastPropertyChange_nothrow(const OUString & _rPropertyName,const Any & _rNewValue,const Any & _rOldValue,bool _bFirstTimeInit) const1628     void OPropertyBrowserController::impl_broadcastPropertyChange_nothrow( const OUString& _rPropertyName, const Any& _rNewValue, const Any& _rOldValue, bool _bFirstTimeInit ) const
1629     {
1630         // are there one or more handlers which are interested in the actuation?
1631         std::pair< PropertyHandlerMultiRepository::const_iterator, PropertyHandlerMultiRepository::const_iterator > aInterestedHandlers =
1632             m_aDependencyHandlers.equal_range( _rPropertyName );
1633         if ( aInterestedHandlers.first == aInterestedHandlers.second )
1634             // none of our handlers is interested in this
1635             return;
1636 
1637         ComposedUIAutoFireGuard aAutoFireGuard( *m_pUIRequestComposer );
1638         try
1639         {
1640             // collect the responses from all interested handlers
1641             PropertyHandlerMultiRepository::const_iterator handler = aInterestedHandlers.first;
1642             while ( handler != aInterestedHandlers.second )
1643             {
1644                 handler->second->actuatingPropertyChanged( _rPropertyName, _rNewValue, _rOldValue,
1645                     m_pUIRequestComposer->getUIForPropertyHandler( handler->second ),
1646                     _bFirstTimeInit );
1647                 ++handler;
1648             }
1649         }
1650         catch( const Exception& )
1651         {
1652             DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1653         }
1654     }
1655 
1656 
1657 } // namespace pcr
1658 
1659 
1660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1661