1
0
Fork 0
libreoffice/svx/source/form/fmPropBrw.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

583 lines
20 KiB
C++

/* -*- 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 <sal/macros.h>
#include <fmprop.hxx>
#include <fmPropBrw.hxx>
#include <svx/strings.hrc>
#include <fmservs.hxx>
#include <fmshimp.hxx>
#include <fmpgeimp.hxx>
#include <svx/dialmgr.hxx>
#include <svx/fmpage.hxx>
#include <svx/fmshell.hxx>
#include <svx/fmview.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svxids.hrc>
#include <com/sun/star/awt/XControlContainer.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/form/XForm.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/form/inspection/DefaultFormComponentInspectorModel.hpp>
#include <com/sun/star/frame/Frame.hpp>
#include <com/sun/star/inspection/ObjectInspector.hpp>
#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
#include <com/sun/star/inspection/DefaultHelpProvider.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/util/VetoException.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/property.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/component_context.hxx>
#include <o3tl/deleter.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/childwin.hxx>
#include <sfx2/objitem.hxx>
#include <sfx2/objsh.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <unotools/confignode.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/weldutils.hxx>
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<FmPropBrw> xControl(new FmPropBrw(::comphelper::getProcessComponentContext(), _pBindings,
this, _pParent->GetFrameWeld(), _pInfo), o3tl::default_delete<FmPropBrw>());
SetController(std::move(xControl));
static_cast<FmPropBrw*>(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<css::awt::XWindow> 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<XNameContainer> 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<XFrame>(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<FmFormShell*>( static_cast<const SfxObjectItem*>(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: */