/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::container; using namespace ::com::sun::star::form; using namespace ::com::sun::star::form::inspection; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::inspection; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::util; using namespace ::svxform; using ::com::sun::star::awt::XWindow; //= FmPropBrwMgr SFX_IMPL_MODELESSDIALOGCONTOLLER(FmPropBrwMgr, SID_FM_SHOW_PROPERTIES) FmPropBrwMgr::FmPropBrwMgr( vcl::Window* _pParent, sal_uInt16 _nId, SfxBindings* _pBindings, const SfxChildWinInfo* _pInfo) :SfxChildWindow(_pParent, _nId) { std::shared_ptr xControl(new FmPropBrw(::comphelper::getProcessComponentContext(), _pBindings, this, _pParent->GetFrameWeld(), _pInfo), o3tl::default_delete()); SetController(std::move(xControl)); static_cast(GetController().get())->Initialize( _pInfo ); } static OUString GetUIHeadlineName(sal_Int16 nClassId, const Any& aUnoObj) { TranslateId pClassNameResourceId; switch ( nClassId ) { case FormComponentType::TEXTFIELD: { Reference< XInterface > xIFace; aUnoObj >>= xIFace; pClassNameResourceId = RID_STR_PROPTITLE_EDIT; if (xIFace.is()) { // we have a chance to check if it's a formatted field model Reference< XServiceInfo > xInfo(xIFace, UNO_QUERY); if (xInfo.is() && (xInfo->supportsService(FM_SUN_COMPONENT_FORMATTEDFIELD))) pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED; else if (!xInfo.is()) { // couldn't distinguish between formatted and edit with the service name, so try with the properties Reference< XPropertySet > xProps(xIFace, UNO_QUERY); if (xProps.is()) { Reference< XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo(); if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER)) pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED; } } } } break; case FormComponentType::COMMANDBUTTON: pClassNameResourceId = RID_STR_PROPTITLE_PUSHBUTTON; break; case FormComponentType::RADIOBUTTON: pClassNameResourceId = RID_STR_PROPTITLE_RADIOBUTTON; break; case FormComponentType::CHECKBOX: pClassNameResourceId = RID_STR_PROPTITLE_CHECKBOX; break; case FormComponentType::LISTBOX: pClassNameResourceId = RID_STR_PROPTITLE_LISTBOX; break; case FormComponentType::COMBOBOX: pClassNameResourceId = RID_STR_PROPTITLE_COMBOBOX; break; case FormComponentType::GROUPBOX: pClassNameResourceId = RID_STR_PROPTITLE_GROUPBOX; break; case FormComponentType::IMAGEBUTTON: pClassNameResourceId = RID_STR_PROPTITLE_IMAGEBUTTON; break; case FormComponentType::FIXEDTEXT: pClassNameResourceId = RID_STR_PROPTITLE_FIXEDTEXT; break; case FormComponentType::GRIDCONTROL: pClassNameResourceId = RID_STR_PROPTITLE_DBGRID; break; case FormComponentType::FILECONTROL: pClassNameResourceId = RID_STR_PROPTITLE_FILECONTROL; break; case FormComponentType::DATEFIELD: pClassNameResourceId = RID_STR_PROPTITLE_DATEFIELD; break; case FormComponentType::TIMEFIELD: pClassNameResourceId = RID_STR_PROPTITLE_TIMEFIELD; break; case FormComponentType::NUMERICFIELD: pClassNameResourceId = RID_STR_PROPTITLE_NUMERICFIELD; break; case FormComponentType::CURRENCYFIELD: pClassNameResourceId = RID_STR_PROPTITLE_CURRENCYFIELD; break; case FormComponentType::PATTERNFIELD: pClassNameResourceId = RID_STR_PROPTITLE_PATTERNFIELD; break; case FormComponentType::IMAGECONTROL: pClassNameResourceId = RID_STR_PROPTITLE_IMAGECONTROL; break; case FormComponentType::HIDDENCONTROL: pClassNameResourceId = RID_STR_PROPTITLE_HIDDEN; break; case FormComponentType::SCROLLBAR: pClassNameResourceId = RID_STR_PROPTITLE_SCROLLBAR; break; case FormComponentType::SPINBUTTON: pClassNameResourceId = RID_STR_PROPTITLE_SPINBUTTON; break; case FormComponentType::NAVIGATIONBAR: pClassNameResourceId = RID_STR_PROPTITLE_NAVBAR; break; case FormComponentType::CONTROL: default: pClassNameResourceId = RID_STR_CONTROL; break; } return SvxResId(pClassNameResourceId); } FmPropBrw::FmPropBrw(const Reference< XComponentContext >& _xORB, SfxBindings* _pBindings, SfxChildWindow* _pMgr, weld::Window* _pParent, const SfxChildWinInfo* _pInfo) : SfxModelessDialogController(_pBindings, _pMgr, _pParent, u"svx/ui/formpropertydialog.ui"_ustr, u"FormPropertyDialog"_ustr) , SfxControllerItem(SID_FM_PROPERTY_CONTROL, *_pBindings) , m_bInitialStateChange(true) , m_pParent(_pParent) , m_nAsyncGetFocusId(nullptr) , m_xDialogBox(m_xBuilder->weld_box(u"dialog-vbox1"_ustr)) , m_xContainer(m_xBuilder->weld_container(u"container"_ustr)) , m_xORB(_xORB) { m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 72, m_xContainer->get_text_height() * 20); try { // create a frame wrapper for myself m_xMeAsFrame = Frame::create(m_xORB); // transport the container area of this dialog to be the container window of the frame css::uno::Reference xFrameContainerWindow(new weld::TransportAsXWindow(m_xContainer.get())); m_xMeAsFrame->initialize(xFrameContainerWindow); m_xMeAsFrame->setName(u"form property browser"_ustr); } catch (const Exception&) { OSL_FAIL("FmPropBrw::FmPropBrw: could not create/initialize my frame!"); m_xMeAsFrame.clear(); } if ( _pInfo ) m_sLastActivePage = _pInfo->aExtraString; } FmPropBrw::~FmPropBrw() { if (m_nAsyncGetFocusId) { Application::RemoveUserEvent(m_nAsyncGetFocusId); m_nAsyncGetFocusId = nullptr; } if (m_xBrowserController.is()) implDetachController(); try { // remove our own properties from the component context. We cannot ensure that the component context // is freed (there might be refcount problems :-\), so at least ensure the context itself // does hold the objects anymore Reference xName(m_xInspectorContext,uno::UNO_QUERY); if ( xName.is() ) { const OUString pProps[] = { u"ContextDocument"_ustr , u"DialogParentWindow"_ustr , u"ControlContext"_ustr , u"ControlShapeAccess"_ustr }; for (const auto & i : pProps) xName->removeByName( i ); } } catch (const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); } ::SfxControllerItem::dispose(); } OUString FmPropBrw::getCurrentPage() const { OUString sCurrentPage; try { if ( m_xBrowserController.is() ) { OSL_VERIFY( m_xBrowserController->getViewData() >>= sCurrentPage ); } if ( sCurrentPage.isEmpty() ) sCurrentPage = m_sLastActivePage; } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while retrieving the current page!"); } return sCurrentPage; } void FmPropBrw::implDetachController() { m_sLastActivePage = getCurrentPage(); implSetNewSelection( InterfaceBag() ); if ( m_xMeAsFrame.is() ) { try { m_xMeAsFrame->setComponent(nullptr, nullptr); } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while resetting the component!"); } } // we attached a frame to the controller manually, so we need to manually tell it that it's detached, too if ( m_xBrowserController.is() ) { m_xBrowserController->attachFrame( nullptr ); } m_xBrowserController.clear(); m_xInspectorModel.clear(); m_xMeAsFrame.clear(); } void FmPropBrw::Close() { // suspend the controller (it is allowed to veto) if ( m_xMeAsFrame.is() ) { try { Reference< XController > xController( m_xMeAsFrame->getController() ); if ( xController.is() && !xController->suspend( true ) ) return; } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while asking the controller!"); } } implDetachController(); // remember our bindings: while we're closed, we're deleted, too, so accessing the bindings after this // would be deadly // 10/19/00 - 79321 - FS SfxBindings& rBindings = SfxControllerItem::GetBindings(); SfxModelessDialogController::Close(); rBindings.Invalidate(SID_FM_CTL_PROPERTIES); rBindings.Invalidate(SID_FM_PROPERTIES); } bool FmPropBrw::implIsReadOnlyModel() const { try { if ( m_xInspectorModel.is() ) return m_xInspectorModel->getIsReadOnly(); return false; } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); } return true; } void FmPropBrw::implSetNewSelection( const InterfaceBag& _rSelection ) { if ( !m_xBrowserController.is() ) return; try { Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW ); // tell it the objects to inspect xInspector->inspect( comphelper::containerToSequence(_rSelection) ); } catch( const VetoException& ) { return; } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("svx.form", ""); return; } // set the new title according to the selected object OUString sTitle; if ( _rSelection.empty() ) { sTitle = SvxResId(RID_STR_NO_PROPERTIES); } else if ( _rSelection.size() > 1 ) { // no form component and (no form or no name) -> Multiselection sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) + SvxResId(RID_STR_PROPTITLE_MULTISELECT); } else { Reference< XPropertySet > xSingleSelection( *_rSelection.begin(), UNO_QUERY); if ( ::comphelper::hasProperty( FM_PROP_CLASSID, xSingleSelection ) ) { sal_Int16 nClassID = FormComponentType::CONTROL; xSingleSelection->getPropertyValue( FM_PROP_CLASSID ) >>= nClassID; sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) + GetUIHeadlineName(nClassID, Any(xSingleSelection)); } else if ( Reference< XForm >( xSingleSelection, UNO_QUERY ).is() ) sTitle = SvxResId(RID_STR_PROPERTIES_FORM); } if ( implIsReadOnlyModel() ) sTitle += SvxResId(RID_STR_READONLY_VIEW); m_xDialog->set_title(sTitle); } void FmPropBrw::FillInfo( SfxChildWinInfo& rInfo ) const { rInfo.bVisible = false; rInfo.aExtraString = getCurrentPage(); } IMPL_LINK_NOARG( FmPropBrw, OnAsyncGetFocus, void*, void ) { m_xDialogBox->child_grab_focus(); m_nAsyncGetFocusId = nullptr; } namespace { bool lcl_shouldEnableHelpSection( const Reference< XComponentContext >& _rxContext ) { ::utl::OConfigurationTreeRoot aConfiguration( ::utl::OConfigurationTreeRoot::createWithComponentContext( _rxContext, u"/org.openoffice.Office.Common/Forms/PropertyBrowser/"_ustr ) ); bool bEnabled = false; OSL_VERIFY( aConfiguration.getNodeValue( u"DirectHelp"_ustr ) >>= bEnabled ); return bEnabled; } } void FmPropBrw::impl_createPropertyBrowser_throw( FmFormShell* _pFormShell ) { // the document in which we live Reference< XInterface > xDocument; if ( _pFormShell && _pFormShell->GetObjectShell() ) xDocument = _pFormShell->GetObjectShell()->GetModel(); // the context of the controls in our document Reference< awt::XControlContainer > xControlContext; if ( _pFormShell && _pFormShell->GetFormView() ) { SdrPageView* pPageView = _pFormShell->GetFormView()->GetSdrPageView(); if(pPageView) { SdrPageWindow* pPageWindow = pPageView->GetPageWindow(0); if(pPageWindow) { xControlContext = pPageWindow->GetControlContainer(); } } } // the default parent window for message boxes Reference< XWindow > xParentWindow(m_xDialog->GetXWindow()); // the mapping from control models to control shapes Reference< XMap > xControlMap; FmFormPage* pFormPage = _pFormShell ? _pFormShell->GetCurPage() : nullptr; if ( pFormPage ) xControlMap = pFormPage->GetImpl().getControlToShapeMap(); // our own component context // a ComponentContext for the ::cppu::ContextEntry_Init aHandlerContextInfo[] = { ::cppu::ContextEntry_Init( u"ContextDocument"_ustr, Any( xDocument ) ), ::cppu::ContextEntry_Init( u"DialogParentWindow"_ustr, Any( xParentWindow ) ), ::cppu::ContextEntry_Init( u"ControlContext"_ustr, Any( xControlContext ) ), ::cppu::ContextEntry_Init( u"ControlShapeAccess"_ustr, Any( xControlMap ) ) }; m_xInspectorContext.set( ::cppu::createComponentContext( aHandlerContextInfo, SAL_N_ELEMENTS( aHandlerContextInfo ), m_xORB ) ); bool bEnableHelpSection = lcl_shouldEnableHelpSection( m_xORB ); // an object inspector model m_xInspectorModel = bEnableHelpSection ? DefaultFormComponentInspectorModel::createWithHelpSection( m_xInspectorContext, 3, 5 ) : DefaultFormComponentInspectorModel::createDefault( m_xInspectorContext ); // an object inspector m_xBrowserController = ObjectInspector::createWithModel( m_xInspectorContext, m_xInspectorModel ); if ( !m_xBrowserController.is() ) { ShowServiceNotAvailableError(m_pParent, u"com.sun.star.inspection.ObjectInspector", true); } else { m_xBrowserController->attachFrame( Reference(m_xMeAsFrame,UNO_QUERY_THROW) ); } if ( bEnableHelpSection ) { Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW ); Reference< XObjectInspectorUI > xInspectorUI( xInspector->getInspectorUI() ); DefaultHelpProvider::create( m_xInspectorContext, xInspectorUI ); } } void FmPropBrw::impl_ensurePropertyBrowser_nothrow( FmFormShell* _pFormShell ) { // the document in which we live Reference< XInterface > xDocument; SfxObjectShell* pObjectShell = _pFormShell ? _pFormShell->GetObjectShell() : nullptr; if ( pObjectShell ) xDocument = pObjectShell->GetModel(); if ( ( xDocument == m_xLastKnownDocument ) && m_xBrowserController.is() ) // nothing to do return; try { // clean up any previous instances of the object inspector if ( m_xMeAsFrame.is() ) m_xMeAsFrame->setComponent( nullptr, nullptr ); else ::comphelper::disposeComponent( m_xBrowserController ); m_xBrowserController.clear(); m_xInspectorModel.clear(); // and create a new one impl_createPropertyBrowser_throw( _pFormShell ); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); } m_xLastKnownDocument = std::move(xDocument); } void FmPropBrw::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) { if (!pState || SID_FM_PROPERTY_CONTROL != nSID) return; try { if (eState >= SfxItemState::DEFAULT) { FmFormShell* pShell = dynamic_cast( static_cast(pState)->GetShell() ); InterfaceBag aSelection; if ( pShell ) pShell->GetImpl()->getCurrentSelection_Lock(aSelection); impl_ensurePropertyBrowser_nothrow( pShell ); // set the new object to inspect implSetNewSelection( aSelection ); // if this is the first time we're here, some additional things need to be done ... if ( m_bInitialStateChange ) { // if we're just newly created, we want to have the focus m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, FmPropBrw, OnAsyncGetFocus)); // and additionally, we want to show the page which was active during // our previous incarnation if ( !m_sLastActivePage.isEmpty() ) { try { if ( m_xBrowserController.is() ) m_xBrowserController->restoreViewData( Any( m_sLastActivePage ) ); } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while setting the initial page!"); } } m_bInitialStateChange = false; } } else { implSetNewSelection( InterfaceBag() ); } } catch (Exception&) { TOOLS_WARN_EXCEPTION("svx.form", ""); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */