1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <svtools/contextmenuhelper.hxx>
22 #include <svtools/menuoptions.hxx>
23 #include <svtools/miscopt.hxx>
25 #include <com/sun/star/frame/XDispatch.hpp>
26 #include <com/sun/star/frame/XDispatchProvider.hpp>
27 #include <com/sun/star/frame/ModuleManager.hpp>
28 #include <com/sun/star/frame/XStatusListener.hpp>
29 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
30 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
31 #include <com/sun/star/ui/XUIConfigurationManager.hpp>
32 #include <com/sun/star/ui/ModuleUIConfigurationManagerSupplier.hpp>
33 #include <com/sun/star/ui/ImageType.hpp>
34 #include <com/sun/star/frame/UICommandDescription.hpp>
35 #include <com/sun/star/util/URLTransformer.hpp>
36 #include <com/sun/star/beans/PropertyValue.hpp>
38 #include <osl/conditn.hxx>
39 #include <cppuhelper/weak.hxx>
40 #include <comphelper/processfactory.hxx>
41 #include <osl/mutex.hxx>
42 #include <vcl/svapp.hxx>
43 #include <vcl/image.hxx>
44 #include <toolkit/helper/vclunohelper.hxx>
45 #include <toolkit/awt/vclxwindow.hxx>
46 #include <toolkit/awt/vclxmenu.hxx>
48 using namespace ::com::sun::star
;
53 // internal helper class to retrieve status updates
54 class StateEventHelper
: public ::com::sun::star::frame::XStatusListener
,
55 public ::cppu::OWeakObject
58 StateEventHelper( const uno::Reference
< frame::XDispatchProvider
>& xDispatchProvider
,
59 const uno::Reference
< util::XURLTransformer
>& xURLTransformer
,
60 const OUString
& aCommandURL
);
61 virtual ~StateEventHelper();
63 bool isCommandEnabled();
66 virtual uno::Any SAL_CALL
queryInterface( const uno::Type
& aType
) throw ( uno::RuntimeException
);
67 virtual void SAL_CALL
acquire() throw ();
68 virtual void SAL_CALL
release() throw ();
71 virtual void SAL_CALL
disposing(const lang::EventObject
& Source
) throw( uno::RuntimeException
);
74 virtual void SAL_CALL
statusChanged(const frame::FeatureStateEvent
& Event
) throw( uno::RuntimeException
);
78 StateEventHelper( const StateEventHelper
& );
79 StateEventHelper
& operator=( const StateEventHelper
& );
81 bool m_bCurrentCommandEnabled
;
82 OUString m_aCommandURL
;
83 uno::Reference
< frame::XDispatchProvider
> m_xDispatchProvider
;
84 uno::Reference
< util::XURLTransformer
> m_xURLTransformer
;
85 osl::Condition m_aCondition
;
88 StateEventHelper::StateEventHelper(
89 const uno::Reference
< frame::XDispatchProvider
>& xDispatchProvider
,
90 const uno::Reference
< util::XURLTransformer
>& xURLTransformer
,
91 const OUString
& rCommandURL
) :
92 m_bCurrentCommandEnabled( true ),
93 m_aCommandURL( rCommandURL
),
94 m_xDispatchProvider( xDispatchProvider
),
95 m_xURLTransformer( xURLTransformer
)
100 StateEventHelper::~StateEventHelper()
103 uno::Any SAL_CALL
StateEventHelper::queryInterface(
104 const uno::Type
& aType
)
105 throw ( uno::RuntimeException
)
107 uno::Any a
= ::cppu::queryInterface(
109 (static_cast< XStatusListener
* >(this)));
114 return ::cppu::OWeakObject::queryInterface( aType
);
117 void SAL_CALL
StateEventHelper::acquire()
120 ::cppu::OWeakObject::acquire();
123 void SAL_CALL
StateEventHelper::release()
126 ::cppu::OWeakObject::release();
129 void SAL_CALL
StateEventHelper::disposing(
130 const lang::EventObject
& )
131 throw ( uno::RuntimeException
)
133 SolarMutexGuard aSolarGuard
;
134 m_xDispatchProvider
.clear();
135 m_xURLTransformer
.clear();
139 void SAL_CALL
StateEventHelper::statusChanged(
140 const frame::FeatureStateEvent
& Event
)
141 throw ( uno::RuntimeException
)
143 SolarMutexGuard aSolarGuard
;
144 m_bCurrentCommandEnabled
= Event
.IsEnabled
;
148 bool StateEventHelper::isCommandEnabled()
150 // Be sure that we cannot die during condition wait
151 uno::Reference
< frame::XStatusListener
> xSelf(
152 (static_cast< frame::XStatusListener
* >(this)));
154 uno::Reference
< frame::XDispatch
> xDispatch
;
155 util::URL aTargetURL
;
157 SolarMutexGuard aSolarGuard
;
158 if ( m_xDispatchProvider
.is() && m_xURLTransformer
.is() )
160 OUString
aSelf( "_self" );
162 aTargetURL
.Complete
= m_aCommandURL
;
163 m_xURLTransformer
->parseStrict( aTargetURL
);
167 xDispatch
= m_xDispatchProvider
->queryDispatch( aTargetURL
, aSelf
, 0 );
169 catch ( uno::RuntimeException
& )
173 catch ( uno::Exception
& )
179 bool bResult( false );
180 if ( xDispatch
.is() )
184 // add/remove ourself to retrieve status by callback
185 xDispatch
->addStatusListener( xSelf
, aTargetURL
);
186 xDispatch
->removeStatusListener( xSelf
, aTargetURL
);
191 catch ( uno::RuntimeException
& )
195 catch ( uno::Exception
& )
199 SolarMutexGuard aSolarGuard
;
200 bResult
= m_bCurrentCommandEnabled
;
206 /*************************************************************************/
210 uno::Reference
< frame::XDispatch
> xDispatch
;
211 util::URL aTargetURL
;
212 uno::Sequence
< beans::PropertyValue
> aArgs
;
215 static const PopupMenu
* lcl_FindPopupFromItemId( const PopupMenu
* pPopupMenu
, sal_uInt16 nItemId
)
219 sal_uInt16 nCount
= pPopupMenu
->GetItemCount();
220 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
222 sal_uInt16 nId
= pPopupMenu
->GetItemId( i
);
223 if ( nId
== nItemId
)
227 const PopupMenu
* pResult( 0 );
229 const PopupMenu
* pSubPopup
= pPopupMenu
->GetPopupMenu( i
);
231 pResult
= lcl_FindPopupFromItemId( pSubPopup
, nItemId
);
241 static OUString
lcl_GetItemCommandRecursive( const PopupMenu
* pPopupMenu
, sal_uInt16 nItemId
)
243 const PopupMenu
* pPopup
= lcl_FindPopupFromItemId( pPopupMenu
, nItemId
);
245 return pPopup
->GetItemCommand( nItemId
);
250 /*************************************************************************/
252 ContextMenuHelper::ContextMenuHelper(
253 const uno::Reference
< frame::XFrame
>& xFrame
,
254 bool bAutoRefresh
) :
255 m_xWeakFrame( xFrame
),
257 m_bAutoRefresh( bAutoRefresh
),
258 m_bUICfgMgrAssociated( false )
262 ContextMenuHelper::~ContextMenuHelper()
267 ContextMenuHelper::completeAndExecute(
269 PopupMenu
& rPopupMenu
)
271 SolarMutexGuard aSolarGuard
;
273 associateUIConfigurationManagers();
274 completeMenuProperties( &rPopupMenu
);
275 executePopupMenu( aPos
, &rPopupMenu
);
280 ContextMenuHelper::completeAndExecute(
282 const uno::Reference
< awt::XPopupMenu
>& xPopupMenu
)
284 SolarMutexGuard aSolarGuard
;
286 VCLXMenu
* pXMenu
= VCLXMenu::GetImplementation( xPopupMenu
);
289 PopupMenu
* pPopupMenu
= dynamic_cast< PopupMenu
* >( pXMenu
->GetMenu() );
290 // as dynamic_cast can return zero check pointer
293 associateUIConfigurationManagers();
294 completeMenuProperties( pPopupMenu
);
295 executePopupMenu( aPos
, pPopupMenu
);
304 ContextMenuHelper::executePopupMenu(
310 uno::Reference
< frame::XFrame
> xFrame( m_xWeakFrame
);
313 uno::Reference
< awt::XWindow
> xWindow( xFrame
->getContainerWindow() );
316 Window
* pParent
= VCLUnoHelper::GetWindow( xWindow
);
317 sal_uInt16 nResult
= pMenu
->Execute( pParent
, rPos
);
321 OUString aCommand
= lcl_GetItemCommandRecursive( pMenu
, nResult
);
322 if ( !aCommand
.isEmpty() )
323 dispatchCommand( xFrame
, aCommand
);
331 ContextMenuHelper::dispatchCommand(
332 const uno::Reference
< ::frame::XFrame
>& rFrame
,
333 const OUString
& aCommandURL
)
335 if ( !m_xURLTransformer
.is() )
337 m_xURLTransformer
= util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
340 util::URL aTargetURL
;
341 aTargetURL
.Complete
= aCommandURL
;
342 m_xURLTransformer
->parseStrict( aTargetURL
);
344 uno::Reference
< frame::XDispatch
> xDispatch
;
345 uno::Reference
< frame::XDispatchProvider
> xDispatchProvider(
346 rFrame
, uno::UNO_QUERY
);
347 if ( xDispatchProvider
.is() )
351 xDispatch
= xDispatchProvider
->queryDispatch( aTargetURL
, m_aSelf
, 0 );
353 catch ( uno::RuntimeException
& )
357 catch ( uno::Exception
& )
362 if ( xDispatch
.is() )
364 ExecuteInfo
* pExecuteInfo
= new ExecuteInfo
;
365 pExecuteInfo
->xDispatch
= xDispatch
;
366 pExecuteInfo
->aTargetURL
= aTargetURL
;
367 pExecuteInfo
->aArgs
= m_aDefaultArgs
;
369 Application::PostUserEvent( STATIC_LINK(0, ContextMenuHelper
, ExecuteHdl_Impl
), pExecuteInfo
);
376 // retrieves and stores references to our user-interface
377 // configuration managers, like image manager, ui command
378 // description manager.
380 ContextMenuHelper::associateUIConfigurationManagers()
382 uno::Reference
< frame::XFrame
> xFrame( m_xWeakFrame
);
383 if ( !m_bUICfgMgrAssociated
&& xFrame
.is() )
385 // clear current state
386 m_xDocImageMgr
.clear();
387 m_xModuleImageMgr
.clear();
388 m_xUICommandLabels
.clear();
392 uno::Reference
< frame::XController
> xController
;
393 uno::Reference
< frame::XModel
> xModel
;
394 xController
= xFrame
->getController();
395 if ( xController
.is() )
396 xModel
= xController
->getModel();
400 // retrieve document image manager form model
401 uno::Reference
< ui::XUIConfigurationManagerSupplier
> xSupplier( xModel
, uno::UNO_QUERY
);
402 if ( xSupplier
.is() )
404 uno::Reference
< ui::XUIConfigurationManager
> xDocUICfgMgr(
405 xSupplier
->getUIConfigurationManager(), uno::UNO_QUERY
);
406 m_xDocImageMgr
= uno::Reference
< ui::XImageManager
>(
407 xDocUICfgMgr
->getImageManager(), uno::UNO_QUERY
);
411 uno::Reference
< frame::XModuleManager2
> xModuleManager(
412 frame::ModuleManager::create( ::comphelper::getProcessComponentContext() ) );
414 uno::Reference
< ui::XImageManager
> xModuleImageManager
;
416 // retrieve module image manager
417 aModuleId
= xModuleManager
->identify( xFrame
);
419 uno::Reference
< ui::XModuleUIConfigurationManagerSupplier
> xModuleCfgMgrSupplier(
420 ui::ModuleUIConfigurationManagerSupplier::create(
421 ::comphelper::getProcessComponentContext() ) );
422 uno::Reference
< ui::XUIConfigurationManager
> xUICfgMgr(
423 xModuleCfgMgrSupplier
->getUIConfigurationManager( aModuleId
));
424 if ( xUICfgMgr
.is() )
426 m_xModuleImageMgr
= uno::Reference
< ui::XImageManager
>(
427 xUICfgMgr
->getImageManager(), uno::UNO_QUERY
);
430 uno::Reference
< container::XNameAccess
> xNameAccess(
431 frame::UICommandDescription::create(
432 ::comphelper::getProcessComponentContext()),
433 uno::UNO_QUERY_THROW
);
436 uno::Any a
= xNameAccess
->getByName( aModuleId
);
437 a
>>= m_xUICommandLabels
;
439 catch ( container::NoSuchElementException
& )
443 catch ( uno::RuntimeException
& )
447 catch ( uno::Exception
& )
449 m_bUICfgMgrAssociated
= true;
452 m_bUICfgMgrAssociated
= true;
459 ContextMenuHelper::getImageFromCommandURL( const OUString
& aCmdURL
) const
462 sal_Int16
nImageType( ui::ImageType::COLOR_NORMAL
|
463 ui::ImageType::SIZE_DEFAULT
);
465 uno::Sequence
< uno::Reference
< graphic::XGraphic
> > aGraphicSeq
;
466 uno::Sequence
< OUString
> aImageCmdSeq( 1 );
467 aImageCmdSeq
[0] = aCmdURL
;
469 if ( m_xDocImageMgr
.is() )
473 aGraphicSeq
= m_xDocImageMgr
->getImages( nImageType
, aImageCmdSeq
);
474 uno::Reference
< graphic::XGraphic
> xGraphic
= aGraphicSeq
[0];
475 aImage
= Image( xGraphic
);
480 catch ( uno::RuntimeException
& )
484 catch ( uno::Exception
& )
489 if ( m_xModuleImageMgr
.is() )
493 aGraphicSeq
= m_xModuleImageMgr
->getImages( nImageType
, aImageCmdSeq
);
494 uno::Reference
< ::com::sun::star::graphic::XGraphic
> xGraphic
= aGraphicSeq
[0];
495 aImage
= Image( xGraphic
);
500 catch ( uno::RuntimeException
& )
504 catch ( uno::Exception
& )
513 ContextMenuHelper::getLabelFromCommandURL(
514 const OUString
& aCmdURL
) const
518 if ( m_xUICommandLabels
.is() )
522 if ( !aCmdURL
.isEmpty() )
525 uno::Sequence
< beans::PropertyValue
> aPropSeq
;
526 uno::Any
a( m_xUICommandLabels
->getByName( aCmdURL
));
527 if ( a
>>= aPropSeq
)
529 for ( sal_Int32 i
= 0; i
< aPropSeq
.getLength(); i
++ )
531 if ( aPropSeq
[i
].Name
== "Label" )
533 aPropSeq
[i
].Value
>>= aStr
;
541 catch ( uno::RuntimeException
& )
544 catch ( uno::Exception
& )
553 ContextMenuHelper::completeMenuProperties(
556 // Retrieve some settings necessary to display complete context
558 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
559 bool bShowMenuImages( rSettings
.GetUseImagesInMenus() );
563 uno::Reference
< frame::XFrame
> xFrame( m_xWeakFrame
);
564 uno::Reference
< frame::XDispatchProvider
> xDispatchProvider( xFrame
, uno::UNO_QUERY
);
566 if ( !m_xURLTransformer
.is() )
568 m_xURLTransformer
= util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
571 for ( sal_uInt16 nPos
= 0; nPos
< pMenu
->GetItemCount(); nPos
++ )
573 sal_uInt16 nId
= pMenu
->GetItemId( nPos
);
574 PopupMenu
* pPopupMenu
= pMenu
->GetPopupMenu( nId
);
576 completeMenuProperties( pPopupMenu
);
577 if ( pMenu
->GetItemType( nPos
) != MENUITEM_SEPARATOR
)
579 OUString
aCmdURL( pMenu
->GetItemCommand( nId
));
581 if ( bShowMenuImages
)
584 if ( !aCmdURL
.isEmpty() )
585 aImage
= getImageFromCommandURL( aCmdURL
);
586 pMenu
->SetItemImage( nId
, aImage
);
589 pMenu
->SetItemImage( nId
, Image() );
591 if (pMenu
->GetItemText(nId
).isEmpty())
593 OUString
aLabel( getLabelFromCommandURL( aCmdURL
));
594 pMenu
->SetItemText( nId
, aLabel
);
597 // Use helper to retrieve state of the command URL
598 StateEventHelper
* pHelper
= new StateEventHelper(
603 uno::Reference
< frame::XStatusListener
> xHelper( pHelper
);
604 pMenu
->EnableItem( nId
, pHelper
->isCommandEnabled() );
611 IMPL_STATIC_LINK_NOINSTANCE( ContextMenuHelper
, ExecuteHdl_Impl
, ExecuteInfo
*, pExecuteInfo
)
613 // Release solar mutex to prevent deadlocks with clipboard thread
614 const sal_uInt32 nRef
= Application::ReleaseSolarMutex();
617 // Asynchronous execution as this can lead to our own destruction while we are
618 // on the stack. Stack unwinding would access the destroyed context menu.
619 pExecuteInfo
->xDispatch
->dispatch( pExecuteInfo
->aTargetURL
, pExecuteInfo
->aArgs
);
621 catch ( uno::Exception
& )
625 // Acquire solar mutex again
626 Application::AcquireSolarMutex( nRef
);
633 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */