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