/************************************************************** * * 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 #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; 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