diff options
Diffstat (limited to 'svtools/source/uno/contextmenuhelper.cxx')
-rw-r--r-- | svtools/source/uno/contextmenuhelper.cxx | 687 |
1 files changed, 687 insertions, 0 deletions
diff --git a/svtools/source/uno/contextmenuhelper.cxx b/svtools/source/uno/contextmenuhelper.cxx new file mode 100644 index 000000000000..9eb36cf18a45 --- /dev/null +++ b/svtools/source/uno/contextmenuhelper.cxx @@ -0,0 +1,687 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include "contextmenuhelper.hxx" +#include <svtools/menuoptions.hxx> +#include <svtools/miscopt.hxx> + +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XModuleManager.hpp> +#include <com/sun/star/frame/XStatusListener.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/XModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/ImageType.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <osl/conditn.hxx> +#include <cppuhelper/weak.hxx> +#include <comphelper/processfactory.hxx> +#include <vos/mutex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/image.hxx> +#include <toolkit/unohlp.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <toolkit/awt/vclxmenu.hxx> + +using namespace ::com::sun::star; + +namespace svt +{ + +// internal helper class to retrieve status updates +class StateEventHelper : public ::com::sun::star::frame::XStatusListener, + public ::cppu::OWeakObject +{ + public: + StateEventHelper( const uno::Reference< frame::XDispatchProvider >& xDispatchProvider, + const uno::Reference< util::XURLTransformer >& xURLTransformer, + const rtl::OUString& aCommandURL ); + virtual ~StateEventHelper(); + + bool isCommandEnabled(); + + // XInterface + virtual uno::Any SAL_CALL queryInterface( const uno::Type& aType ) throw ( uno::RuntimeException); + virtual void SAL_CALL acquire() throw (); + virtual void SAL_CALL release() throw (); + + // XEventListener + virtual void SAL_CALL disposing(const lang::EventObject& Source) throw( uno::RuntimeException ); + + // XStatusListener + virtual void SAL_CALL statusChanged(const frame::FeatureStateEvent& Event) throw( uno::RuntimeException ); + + private: + StateEventHelper(); + StateEventHelper( const StateEventHelper& ); + StateEventHelper& operator=( const StateEventHelper& ); + + bool m_bCurrentCommandEnabled; + ::rtl::OUString m_aCommandURL; + uno::Reference< frame::XDispatchProvider > m_xDispatchProvider; + uno::Reference< util::XURLTransformer > m_xURLTransformer; + osl::Condition m_aCondition; +}; + +StateEventHelper::StateEventHelper( + const uno::Reference< frame::XDispatchProvider >& xDispatchProvider, + const uno::Reference< util::XURLTransformer >& xURLTransformer, + const rtl::OUString& rCommandURL ) : + m_bCurrentCommandEnabled( true ), + m_aCommandURL( rCommandURL ), + m_xDispatchProvider( xDispatchProvider ), + m_xURLTransformer( xURLTransformer ) +{ + m_aCondition.reset(); +} + +StateEventHelper::~StateEventHelper() +{} + +uno::Any SAL_CALL StateEventHelper::queryInterface( + const uno::Type& aType ) +throw ( uno::RuntimeException ) +{ + uno::Any a = ::cppu::queryInterface( + aType, + SAL_STATIC_CAST( XStatusListener*, this )); + + if( a.hasValue() ) + return a; + + return ::cppu::OWeakObject::queryInterface( aType ); +} + +void SAL_CALL StateEventHelper::acquire() +throw () +{ + ::cppu::OWeakObject::acquire(); +} + +void SAL_CALL StateEventHelper::release() +throw () +{ + ::cppu::OWeakObject::release(); +} + +void SAL_CALL StateEventHelper::disposing( + const lang::EventObject& ) +throw ( uno::RuntimeException ) +{ + vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + m_xDispatchProvider.clear(); + m_xURLTransformer.clear(); + m_aCondition.set(); +} + +void SAL_CALL StateEventHelper::statusChanged( + const frame::FeatureStateEvent& Event ) +throw ( uno::RuntimeException ) +{ + vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + m_bCurrentCommandEnabled = Event.IsEnabled; + m_aCondition.set(); +} + +bool StateEventHelper::isCommandEnabled() +{ + // Be sure that we cannot die during condition wait + uno::Reference< frame::XStatusListener > xSelf( + SAL_STATIC_CAST( frame::XStatusListener*, this )); + + uno::Reference< frame::XDispatch > xDispatch; + util::URL aTargetURL; + { + vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + if ( m_xDispatchProvider.is() && m_xURLTransformer.is() ) + { + ::rtl::OUString aSelf( RTL_CONSTASCII_USTRINGPARAM( "_self" )); + + aTargetURL.Complete = m_aCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + try + { + xDispatch = m_xDispatchProvider->queryDispatch( aTargetURL, aSelf, 0 ); + } + catch ( uno::RuntimeException& ) + { + throw; + } + catch ( uno::Exception& ) + { + } + } + } + + bool bResult( false ); + if ( xDispatch.is() ) + { + try + { + // add/remove ourself to retrieve status by callback + xDispatch->addStatusListener( xSelf, aTargetURL ); + xDispatch->removeStatusListener( xSelf, aTargetURL ); + + // wait for anwser + m_aCondition.wait(); + } + catch ( uno::RuntimeException& ) + { + throw; + } + catch ( uno::Exception& ) + { + } + + vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + bResult = m_bCurrentCommandEnabled; + } + + return bResult; +} + +/*************************************************************************/ + +struct ExecuteInfo +{ + uno::Reference< frame::XDispatch > xDispatch; + util::URL aTargetURL; + uno::Sequence< beans::PropertyValue > aArgs; +}; + +static const PopupMenu* lcl_FindPopupFromItemId( const PopupMenu* pPopupMenu, sal_uInt16 nItemId ) +{ + if ( pPopupMenu ) + { + sal_uInt16 nCount = pPopupMenu->GetItemCount(); + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + sal_uInt16 nId = pPopupMenu->GetItemId( i ); + if ( nId == nItemId ) + return pPopupMenu; + else + { + const PopupMenu* pResult( 0 ); + + const PopupMenu* pSubPopup = pPopupMenu->GetPopupMenu( i ); + if ( pPopupMenu ) + pResult = lcl_FindPopupFromItemId( pSubPopup, nItemId ); + if ( pResult != 0 ) + return pResult; + } + } + } + + return NULL; +} + +static ::rtl::OUString lcl_GetItemCommandRecursive( const PopupMenu* pPopupMenu, sal_uInt16 nItemId ) +{ + const PopupMenu* pPopup = lcl_FindPopupFromItemId( pPopupMenu, nItemId ); + if ( pPopup ) + return pPopup->GetItemCommand( nItemId ); + else + return ::rtl::OUString(); +} + +/*************************************************************************/ + +ContextMenuHelper::ContextMenuHelper( + const uno::Reference< frame::XFrame >& xFrame, + bool bAutoRefresh ) : + m_xWeakFrame( xFrame ), + m_aSelf( RTL_CONSTASCII_USTRINGPARAM( "_self" )), + m_bAutoRefresh( bAutoRefresh ), + m_bUICfgMgrAssociated( false ) +{ +} + +ContextMenuHelper::~ContextMenuHelper() +{ +} + +void +ContextMenuHelper::completeAndExecute( + const Point& aPos, + PopupMenu& rPopupMenu ) +{ + vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + + associateUIConfigurationManagers(); + completeMenuProperties( &rPopupMenu ); + executePopupMenu( aPos, &rPopupMenu ); + resetAssociations(); +} + +void +ContextMenuHelper::completeAndExecute( + const Point& aPos, + const uno::Reference< awt::XPopupMenu >& xPopupMenu ) +{ + vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + + VCLXMenu* pXMenu = VCLXMenu::GetImplementation( xPopupMenu ); + if ( pXMenu ) + { + PopupMenu* pPopupMenu = dynamic_cast< PopupMenu* >( pXMenu->GetMenu() ); + // as dynamic_cast can return zero check pointer + if ( pPopupMenu ) + { + associateUIConfigurationManagers(); + completeMenuProperties( pPopupMenu ); + executePopupMenu( aPos, pPopupMenu ); + resetAssociations(); + } + } +} + +uno::Reference< awt::XPopupMenu > +ContextMenuHelper::create( + const ::rtl::OUString& /*aPopupMenuResourceId*/ ) +{ + // NOT IMPLEMENTED YET! + return uno::Reference< awt::XPopupMenu >(); +} + +bool +ContextMenuHelper::createAndExecute( + const Point& /*aPos*/, + const ::rtl::OUString& /*aPopupMenuResourceId*/ ) +{ + // NOT IMPLEMENTED YET! + return false; +} + +// private member + +void +ContextMenuHelper::executePopupMenu( + const Point& rPos, + PopupMenu* pMenu ) +{ + if ( pMenu ) + { + uno::Reference< frame::XFrame > xFrame( m_xWeakFrame ); + if ( xFrame.is() ) + { + uno::Reference< awt::XWindow > xWindow( xFrame->getContainerWindow() ); + if ( xWindow.is() ) + { + Window* pParent = VCLUnoHelper::GetWindow( xWindow ); + sal_uInt16 nResult = pMenu->Execute( pParent, rPos ); + + if ( nResult > 0 ) + { + ::rtl::OUString aCommand = lcl_GetItemCommandRecursive( pMenu, nResult ); + if ( aCommand.getLength() > 0 ) + dispatchCommand( xFrame, aCommand ); + } + } + } + } +} + +bool +ContextMenuHelper::dispatchCommand( + const uno::Reference< ::frame::XFrame >& rFrame, + const ::rtl::OUString& aCommandURL ) +{ + if ( !m_xURLTransformer.is() ) + { + m_xURLTransformer = uno::Reference< util::XURLTransformer >( + ::comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.util.URLTransformer" ))), + uno::UNO_QUERY ); + } + + util::URL aTargetURL; + uno::Reference< frame::XDispatch > xDispatch; + if ( m_xURLTransformer.is() ) + { + aTargetURL.Complete = aCommandURL; + m_xURLTransformer->parseStrict( aTargetURL ); + + uno::Reference< frame::XDispatchProvider > xDispatchProvider( + rFrame, uno::UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + try + { + xDispatch = xDispatchProvider->queryDispatch( aTargetURL, m_aSelf, 0 ); + } + catch ( uno::RuntimeException& ) + { + throw; + } + catch ( uno::Exception& ) + { + } + } + } + + if ( xDispatch.is() ) + { + ExecuteInfo* pExecuteInfo = new ExecuteInfo; + pExecuteInfo->xDispatch = xDispatch; + pExecuteInfo->aTargetURL = aTargetURL; + pExecuteInfo->aArgs = m_aDefaultArgs; + + Application::PostUserEvent( STATIC_LINK(0, ContextMenuHelper , ExecuteHdl_Impl), pExecuteInfo ); + return true; + } + + return false; +} + +// retrieves and stores references to our user-interface +// configuration managers, like image manager, ui command +// description manager. +bool +ContextMenuHelper::associateUIConfigurationManagers() +{ + uno::Reference< frame::XFrame > xFrame( m_xWeakFrame ); + if ( !m_bUICfgMgrAssociated && xFrame.is() ) + { + // clear current state + m_xDocImageMgr.clear(); + m_xModuleImageMgr.clear(); + m_xUICommandLabels.clear(); + + try + { + uno::Reference < frame::XController > xController; + uno::Reference < frame::XModel > xModel; + xController = xFrame->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + + if ( xModel.is() ) + { + // retrieve document image manager form model + uno::Reference< ui::XUIConfigurationManagerSupplier > xSupplier( xModel, uno::UNO_QUERY ); + if ( xSupplier.is() ) + { + uno::Reference< ui::XUIConfigurationManager > xDocUICfgMgr( + xSupplier->getUIConfigurationManager(), uno::UNO_QUERY ); + m_xDocImageMgr = uno::Reference< ui::XImageManager >( + xDocUICfgMgr->getImageManager(), uno::UNO_QUERY ); + } + } + + uno::Reference< frame::XModuleManager > xModuleManager( + ::comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.frame.ModuleManager" ))), + uno::UNO_QUERY ); + + uno::Reference< ui::XImageManager > xModuleImageManager; + rtl::OUString aModuleId; + if ( xModuleManager.is() ) + { + // retrieve module image manager + aModuleId = xModuleManager->identify( xFrame ); + + uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier( + ::comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.ui.ModuleUIConfigurationManagerSupplier" ))), + uno::UNO_QUERY ); + if ( xModuleCfgMgrSupplier.is() ) + { + uno::Reference< ui::XUIConfigurationManager > xUICfgMgr( + xModuleCfgMgrSupplier->getUIConfigurationManager( aModuleId )); + if ( xUICfgMgr.is() ) + { + m_xModuleImageMgr = uno::Reference< ui::XImageManager >( + xUICfgMgr->getImageManager(), uno::UNO_QUERY ); + } + } + } + + uno::Reference< container::XNameAccess > xNameAccess( + ::comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.frame.UICommandDescription" ))), + uno::UNO_QUERY ); + if ( xNameAccess.is() ) + { + try + { + uno::Any a = xNameAccess->getByName( aModuleId ); + a >>= m_xUICommandLabels; + } + catch ( container::NoSuchElementException& ) + { + } + } + } + catch ( uno::RuntimeException& ) + { + throw; + } + catch ( uno::Exception& ) + { + m_bUICfgMgrAssociated = true; + return false; + } + m_bUICfgMgrAssociated = true; + } + + return true; +} + +Image +ContextMenuHelper::getImageFromCommandURL( + const ::rtl::OUString& aCmdURL, + bool bHiContrast ) const +{ + Image aImage; + sal_Int16 nImageType( ui::ImageType::COLOR_NORMAL| + ui::ImageType::SIZE_DEFAULT ); + if ( bHiContrast ) + nImageType |= ui::ImageType::COLOR_HIGHCONTRAST; + + uno::Sequence< uno::Reference< graphic::XGraphic > > aGraphicSeq; + uno::Sequence< ::rtl::OUString > aImageCmdSeq( 1 ); + aImageCmdSeq[0] = aCmdURL; + + if ( m_xDocImageMgr.is() ) + { + try + { + aGraphicSeq = m_xDocImageMgr->getImages( nImageType, aImageCmdSeq ); + uno::Reference< graphic::XGraphic > xGraphic = aGraphicSeq[0]; + aImage = Image( xGraphic ); + + if ( !!aImage ) + return aImage; + } + catch ( uno::RuntimeException& ) + { + throw; + } + catch ( uno::Exception& ) + { + } + } + + if ( m_xModuleImageMgr.is() ) + { + try + { + aGraphicSeq = m_xModuleImageMgr->getImages( nImageType, aImageCmdSeq ); + uno::Reference< ::com::sun::star::graphic::XGraphic > xGraphic = aGraphicSeq[0]; + aImage = Image( xGraphic ); + + if ( !!aImage ) + return aImage; + } + catch ( uno::RuntimeException& ) + { + throw; + } + catch ( uno::Exception& ) + { + } + } + + return aImage; +} + +rtl::OUString +ContextMenuHelper::getLabelFromCommandURL( + const ::rtl::OUString& aCmdURL ) const +{ + ::rtl::OUString aLabel; + + if ( m_xUICommandLabels.is() ) + { + try + { + if ( aCmdURL.getLength() > 0 ) + { + rtl::OUString aStr; + uno::Sequence< beans::PropertyValue > aPropSeq; + uno::Any a( m_xUICommandLabels->getByName( aCmdURL )); + if ( a >>= aPropSeq ) + { + for ( sal_Int32 i = 0; i < aPropSeq.getLength(); i++ ) + { + if ( aPropSeq[i].Name.equalsAscii( "Label" )) + { + aPropSeq[i].Value >>= aStr; + break; + } + } + } + aLabel = aStr; + } + } + catch ( uno::RuntimeException& ) + { + } + catch ( uno::Exception& ) + { + } + } + + return aLabel; +} + +void +ContextMenuHelper::completeMenuProperties( + Menu* pMenu ) +{ + // Retrieve some settings necessary to display complete context + // menu correctly. + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + bool bShowMenuImages( rSettings.GetUseImagesInMenus() ); + bool bIsHiContrast( rSettings.GetHighContrastMode() ); + + if ( pMenu ) + { + uno::Reference< frame::XFrame > xFrame( m_xWeakFrame ); + uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, uno::UNO_QUERY ); + + if ( !m_xURLTransformer.is() ) + { + m_xURLTransformer = uno::Reference< util::XURLTransformer >( + ::comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.util.URLTransformer" ))), + uno::UNO_QUERY ); + } + + for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ ) + { + sal_uInt16 nId = pMenu->GetItemId( nPos ); + PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nId ); + if ( pPopupMenu ) + completeMenuProperties( pPopupMenu ); + if ( pMenu->GetItemType( nPos ) != MENUITEM_SEPARATOR ) + { + ::rtl::OUString aCmdURL( pMenu->GetItemCommand( nId )); + + if ( bShowMenuImages ) + { + Image aImage; + if ( aCmdURL.getLength() > 0 ) + aImage = getImageFromCommandURL( aCmdURL, bIsHiContrast ); + pMenu->SetItemImage( nId, aImage ); + } + else + pMenu->SetItemImage( nId, Image() ); + + if ( pMenu->GetItemText( nId ).Len() == 0 ) + { + ::rtl::OUString aLabel( getLabelFromCommandURL( aCmdURL )); + pMenu->SetItemText( nId, aLabel ); + } + + // Use helper to retrieve state of the command URL + StateEventHelper* pHelper = new StateEventHelper( + xDispatchProvider, + m_xURLTransformer, + aCmdURL ); + + uno::Reference< frame::XStatusListener > xHelper( pHelper ); + pMenu->EnableItem( nId, pHelper->isCommandEnabled() ); + } + } + } +} + + +IMPL_STATIC_LINK_NOINSTANCE( ContextMenuHelper, ExecuteHdl_Impl, ExecuteInfo*, pExecuteInfo ) +{ + // Release solar mutex to prevent deadlocks with clipboard thread + const sal_uInt32 nRef = Application::ReleaseSolarMutex(); + try + { + // Asynchronous execution as this can lead to our own destruction while we are + // on the stack. Stack unwinding would access the destroyed context menu. + pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs ); + } + catch ( uno::Exception& ) + { + } + + // Acquire solar mutex again + Application::AcquireSolarMutex( nRef ); + delete pExecuteInfo; + return 0; +} + +} // namespace svt |