| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_svtools.hxx" |
| |
| #include <svtools/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 |