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