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 .
20 #include <uielement/newmenucontroller.hxx>
22 #include <threadhelp/resetableguard.hxx>
24 #include <classes/resource.hrc>
25 #include <classes/fwkresid.hxx>
26 #include <framework/bmkmenu.hxx>
27 #include <framework/imageproducer.hxx>
28 #include <framework/menuconfiguration.hxx>
30 #include <com/sun/star/awt/XDevice.hpp>
31 #include <com/sun/star/beans/PropertyValue.hpp>
32 #include <com/sun/star/awt/MenuItemStyle.hpp>
33 #include <com/sun/star/ui/ModuleUIConfigurationManagerSupplier.hpp>
34 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
35 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
36 #include <com/sun/star/frame/ModuleManager.hpp>
38 #include <vcl/svapp.hxx>
39 #include <vcl/i18nhelp.hxx>
40 #include <rtl/ustrbuf.hxx>
41 #include <cppuhelper/implbase1.hxx>
42 #include <osl/file.hxx>
43 #include <svtools/menuoptions.hxx>
44 #include <svtools/acceleratorexecute.hxx>
45 #include <unotools/moduleoptions.hxx>
46 #include <osl/mutex.hxx>
48 //_________________________________________________________________________________________________________________
50 //_________________________________________________________________________________________________________________
52 using namespace com::sun::star::uno
;
53 using namespace com::sun::star::lang
;
54 using namespace com::sun::star::frame
;
55 using namespace com::sun::star::beans
;
56 using namespace com::sun::star::util
;
57 using namespace com::sun::star::container
;
58 using namespace com::sun::star::ui
;
60 static const char SFX_REFERER_USER
[] = "private:user";
65 DEFINE_XSERVICEINFO_MULTISERVICE ( NewMenuController
,
67 SERVICENAME_POPUPMENUCONTROLLER
,
68 IMPLEMENTATIONNAME_NEWMENUCONTROLLER
71 DEFINE_INIT_SERVICE ( NewMenuController
, {} )
73 void NewMenuController::setMenuImages( PopupMenu
* pPopupMenu
, sal_Bool bSetImages
)
75 sal_uInt16 nItemCount
= pPopupMenu
->GetItemCount();
77 Reference
< XFrame
> xFrame( m_xFrame
);
79 for ( sal_uInt16 i
= 0; i
< nItemCount
; i
++ )
81 sal_uInt16 nItemId
= pPopupMenu
->GetItemId( sal::static_int_cast
<sal_uInt16
>( i
));
86 sal_Bool
bImageSet( sal_False
);
89 AddInfoForId::const_iterator pInfo
= m_aAddInfoForItem
.find( nItemId
);
90 if ( pInfo
!= m_aAddInfoForItem
.end() )
91 aImageId
= pInfo
->second
.aImageId
; // Retrieve image id for menu item
93 if ( !aImageId
.isEmpty() )
95 aImage
= GetImageFromURL( xFrame
, aImageId
, false );
99 pPopupMenu
->SetItemImage( nItemId
, aImage
);
105 String
aCmd( pPopupMenu
->GetItemCommand( nItemId
) );
107 aImage
= GetImageFromURL( xFrame
, aCmd
, false );
110 pPopupMenu
->SetItemImage( nItemId
, aImage
);
114 pPopupMenu
->SetItemImage( nItemId
, aImage
);
119 void NewMenuController::determineAndSetNewDocAccel( PopupMenu
* pPopupMenu
, const KeyCode
& rKeyCode
)
121 sal_uInt16
nCount( pPopupMenu
->GetItemCount() );
123 sal_Bool
bFound( sal_False
);
126 if ( !m_aEmptyDocURL
.isEmpty() )
128 // Search for the empty document URL
130 for ( sal_uInt32 i
= 0; i
< sal_uInt32( nCount
); i
++ )
132 nId
= pPopupMenu
->GetItemId( sal_uInt16( i
));
133 if ( nId
!= 0 && pPopupMenu
->GetItemType( nId
) != MENUITEM_SEPARATOR
)
135 aCommand
= pPopupMenu
->GetItemCommand( nId
);
136 if ( aCommand
.indexOf( m_aEmptyDocURL
) == 0 )
138 pPopupMenu
->SetAccelKey( nId
, rKeyCode
);
148 // Search for the default module name
149 OUString
aDefaultModuleName( SvtModuleOptions().GetDefaultModuleName() );
150 if ( !aDefaultModuleName
.isEmpty() )
152 for ( sal_uInt32 i
= 0; i
< sal_uInt32( nCount
); i
++ )
154 nId
= pPopupMenu
->GetItemId( sal_uInt16( i
));
155 if ( nId
!= 0 && pPopupMenu
->GetItemType( nId
) != MENUITEM_SEPARATOR
)
157 aCommand
= pPopupMenu
->GetItemCommand( nId
);
158 if ( aCommand
.indexOf( aDefaultModuleName
) >= 0 )
160 pPopupMenu
->SetAccelKey( nId
, rKeyCode
);
169 void NewMenuController::setAccelerators( PopupMenu
* pPopupMenu
)
171 if ( m_bModuleIdentified
)
173 Reference
< XAcceleratorConfiguration
> xDocAccelCfg( m_xDocAcceleratorManager
);
174 Reference
< XAcceleratorConfiguration
> xModuleAccelCfg( m_xModuleAcceleratorManager
);
175 Reference
< XAcceleratorConfiguration
> xGlobalAccelCfg( m_xGlobalAcceleratorManager
);
177 if ( !m_bAcceleratorCfg
)
179 // Retrieve references on demand
180 m_bAcceleratorCfg
= sal_True
;
181 if ( !xDocAccelCfg
.is() )
183 Reference
< XController
> xController
= m_xFrame
->getController();
184 Reference
< XModel
> xModel
;
185 if ( xController
.is() )
187 xModel
= xController
->getModel();
190 Reference
< XUIConfigurationManagerSupplier
> xSupplier( xModel
, UNO_QUERY
);
191 if ( xSupplier
.is() )
193 Reference
< XUIConfigurationManager
> xDocUICfgMgr( xSupplier
->getUIConfigurationManager(), UNO_QUERY
);
194 if ( xDocUICfgMgr
.is() )
196 xDocAccelCfg
= Reference
< XAcceleratorConfiguration
>( xDocUICfgMgr
->getShortCutManager(), UNO_QUERY
);
197 m_xDocAcceleratorManager
= xDocAccelCfg
;
204 if ( !xModuleAccelCfg
.is() )
206 Reference
< XModuleUIConfigurationManagerSupplier
> xModuleCfgMgrSupplier
=
207 ModuleUIConfigurationManagerSupplier::create( comphelper::getComponentContext(m_xServiceManager
) );
208 Reference
< XUIConfigurationManager
> xUICfgMgr
= xModuleCfgMgrSupplier
->getUIConfigurationManager( m_aModuleIdentifier
);
209 if ( xUICfgMgr
.is() )
211 xModuleAccelCfg
= Reference
< XAcceleratorConfiguration
>( xUICfgMgr
->getShortCutManager(), UNO_QUERY
);
212 m_xModuleAcceleratorManager
= xModuleAccelCfg
;
216 if ( !xGlobalAccelCfg
.is() )
218 xGlobalAccelCfg
= GlobalAcceleratorConfiguration::create( comphelper::getComponentContext(m_xServiceManager
) );
219 m_xGlobalAcceleratorManager
= xGlobalAccelCfg
;
223 KeyCode aEmptyKeyCode
;
224 sal_uInt32
nItemCount( pPopupMenu
->GetItemCount() );
225 std::vector
< KeyCode
> aMenuShortCuts
;
226 std::vector
< OUString
> aCmds
;
227 std::vector
< sal_uInt32
> aIds
;
228 for ( sal_uInt32 i
= 0; i
< nItemCount
; i
++ )
230 sal_uInt16
nId( pPopupMenu
->GetItemId( sal_uInt16( i
)));
231 if ( nId
& ( pPopupMenu
->GetItemType( nId
) != MENUITEM_SEPARATOR
))
233 aIds
.push_back( nId
);
234 aMenuShortCuts
.push_back( aEmptyKeyCode
);
235 aCmds
.push_back( pPopupMenu
->GetItemCommand( nId
));
239 sal_uInt32
nSeqCount( aIds
.size() );
244 Sequence
< OUString
> aSeq( nSeqCount
);
246 // Add a special command for our "New" menu.
249 aSeq
[nSeqCount
-1] = m_aCommandURL
;
250 aMenuShortCuts
.push_back( aEmptyKeyCode
);
253 const sal_uInt32 nCount
= aCmds
.size();
254 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
257 if ( m_xGlobalAcceleratorManager
.is() )
258 retrieveShortcutsFromConfiguration( xGlobalAccelCfg
, aSeq
, aMenuShortCuts
);
259 if ( m_xModuleAcceleratorManager
.is() )
260 retrieveShortcutsFromConfiguration( xModuleAccelCfg
, aSeq
, aMenuShortCuts
);
261 if ( m_xDocAcceleratorManager
.is() )
262 retrieveShortcutsFromConfiguration( xGlobalAccelCfg
, aSeq
, aMenuShortCuts
);
264 const sal_uInt32 nCount2
= aIds
.size();
265 for ( sal_uInt32 i
= 0; i
< nCount2
; i
++ )
266 pPopupMenu
->SetAccelKey( sal_uInt16( aIds
[i
] ), aMenuShortCuts
[i
] );
268 // Special handling for "New" menu short-cut should be set at the
269 // document which will be opened using it.
272 if ( aMenuShortCuts
[nSeqCount
-1] != aEmptyKeyCode
)
273 determineAndSetNewDocAccel( pPopupMenu
, aMenuShortCuts
[nSeqCount
-1] );
278 void NewMenuController::retrieveShortcutsFromConfiguration(
279 const Reference
< XAcceleratorConfiguration
>& rAccelCfg
,
280 const Sequence
< OUString
>& rCommands
,
281 std::vector
< KeyCode
>& aMenuShortCuts
)
283 if ( rAccelCfg
.is() )
287 com::sun::star::awt::KeyEvent aKeyEvent
;
288 Sequence
< Any
> aSeqKeyCode
= rAccelCfg
->getPreferredKeyEventsForCommandList( rCommands
);
289 for ( sal_Int32 i
= 0; i
< aSeqKeyCode
.getLength(); i
++ )
291 if ( aSeqKeyCode
[i
] >>= aKeyEvent
)
292 aMenuShortCuts
[i
] = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent
);
295 catch ( const IllegalArgumentException
& )
301 NewMenuController::NewMenuController( const ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XMultiServiceFactory
>& xServiceManager
) :
302 svt::PopupMenuControllerBase( xServiceManager
),
303 m_bShowImages( sal_True
),
304 m_bNewMenu( sal_False
),
305 m_bModuleIdentified( sal_False
),
306 m_bAcceleratorCfg( sal_False
),
307 m_aTargetFrame( "_default" )
311 NewMenuController::~NewMenuController()
316 void NewMenuController::fillPopupMenu( Reference
< css::awt::XPopupMenu
>& rPopupMenu
)
318 VCLXPopupMenu
* pPopupMenu
= (VCLXPopupMenu
*)VCLXMenu::GetImplementation( rPopupMenu
);
319 PopupMenu
* pVCLPopupMenu
= 0;
321 SolarMutexGuard aSolarMutexGuard
;
323 resetPopupMenu( rPopupMenu
);
325 pVCLPopupMenu
= (PopupMenu
*)pPopupMenu
->GetMenu();
329 MenuConfiguration
aMenuCfg( comphelper::getComponentContext(m_xServiceManager
) );
330 BmkMenu
* pSubMenu( 0 );
333 pSubMenu
= (BmkMenu
*)aMenuCfg
.CreateBookmarkMenu( m_xFrame
, BOOKMARK_NEWMENU
);
335 pSubMenu
= (BmkMenu
*)aMenuCfg
.CreateBookmarkMenu( m_xFrame
, BOOKMARK_WIZARDMENU
);
337 // copy entries as we have to use the provided popup menu
338 *pVCLPopupMenu
= *pSubMenu
;
343 // retrieve additional parameters from bookmark menu and
344 // store it in a boost::unordered_map.
345 for ( sal_uInt16 i
= 0; i
< pSubMenu
->GetItemCount(); i
++ )
347 sal_uInt16 nItemId
= pSubMenu
->GetItemId( sal::static_int_cast
<sal_uInt16
>( i
) );
348 if (( nItemId
!= 0 ) &&
349 ( pSubMenu
->GetItemType( nItemId
) != MENUITEM_SEPARATOR
))
351 MenuConfiguration::Attributes
* pBmkAttributes
= (MenuConfiguration::Attributes
*)(pSubMenu
->GetUserValue( nItemId
));
352 if ( pBmkAttributes
!= 0 )
354 aAddInfo
.aTargetFrame
= pBmkAttributes
->aTargetFrame
;
355 aAddInfo
.aImageId
= pBmkAttributes
->aImageId
;
357 m_aAddInfoForItem
.insert( AddInfoForId::value_type( nItemId
, aAddInfo
));
363 setMenuImages( pVCLPopupMenu
, m_bShowImages
);
370 void SAL_CALL
NewMenuController::disposing( const EventObject
& ) throw ( RuntimeException
)
372 Reference
< css::awt::XMenuListener
> xHolder(( OWeakObject
*)this, UNO_QUERY
);
374 osl::MutexGuard
aLock( m_aMutex
);
377 m_xServiceManager
.clear();
379 if ( m_xPopupMenu
.is() )
380 m_xPopupMenu
->removeMenuListener( Reference
< css::awt::XMenuListener
>(( OWeakObject
*)this, UNO_QUERY
));
381 m_xPopupMenu
.clear();
385 void SAL_CALL
NewMenuController::statusChanged( const FeatureStateEvent
& ) throw ( RuntimeException
)
390 void SAL_CALL
NewMenuController::select( const css::awt::MenuEvent
& rEvent
) throw (RuntimeException
)
392 Reference
< css::awt::XPopupMenu
> xPopupMenu
;
393 Reference
< XDispatch
> xDispatch
;
394 Reference
< XDispatchProvider
> xDispatchProvider
;
395 Reference
< XMultiServiceFactory
> xServiceManager
;
396 Reference
< XURLTransformer
> xURLTransformer
;
398 osl::ClearableMutexGuard
aLock( m_aMutex
);
399 xPopupMenu
= m_xPopupMenu
;
400 xDispatchProvider
= Reference
< XDispatchProvider
>( m_xFrame
, UNO_QUERY
);
401 xServiceManager
= m_xServiceManager
;
402 xURLTransformer
= m_xURLTransformer
;
405 css::util::URL aTargetURL
;
406 Sequence
< PropertyValue
> aArgsList( 1 );
408 if ( xPopupMenu
.is() && xDispatchProvider
.is() )
410 VCLXPopupMenu
* pPopupMenu
= (VCLXPopupMenu
*)VCLXPopupMenu::GetImplementation( xPopupMenu
);
414 SolarMutexGuard aSolarMutexGuard
;
415 PopupMenu
* pVCLPopupMenu
= (PopupMenu
*)pPopupMenu
->GetMenu();
416 aTargetURL
.Complete
= pVCLPopupMenu
->GetItemCommand( rEvent
.MenuId
);
419 xURLTransformer
->parseStrict( aTargetURL
);
421 aArgsList
[0].Name
= OUString( "Referer" );
422 aArgsList
[0].Value
= makeAny( OUString(SFX_REFERER_USER
));
424 OUString
aTargetFrame( m_aTargetFrame
);
425 AddInfoForId::const_iterator pItem
= m_aAddInfoForItem
.find( rEvent
.MenuId
);
426 if ( pItem
!= m_aAddInfoForItem
.end() )
427 aTargetFrame
= pItem
->second
.aTargetFrame
;
429 xDispatch
= xDispatchProvider
->queryDispatch( aTargetURL
, aTargetFrame
, 0 );
433 if ( xDispatch
.is() )
435 // Call dispatch asychronously as we can be destroyed while dispatch is
436 // executed. VCL is not able to survive this as it wants to call listeners
438 NewDocument
* pNewDocument
= new NewDocument
;
439 pNewDocument
->xDispatch
= xDispatch
;
440 pNewDocument
->aTargetURL
= aTargetURL
;
441 pNewDocument
->aArgSeq
= aArgsList
;
442 Application::PostUserEvent( STATIC_LINK(0, NewMenuController
, ExecuteHdl_Impl
), pNewDocument
);
446 void SAL_CALL
NewMenuController::activate( const css::awt::MenuEvent
& ) throw (RuntimeException
)
448 SolarMutexGuard aSolarMutexGuard
;
449 if ( m_xFrame
.is() && m_xPopupMenu
.is() )
451 VCLXPopupMenu
* pPopupMenu
= (VCLXPopupMenu
*)VCLXPopupMenu::GetImplementation( m_xPopupMenu
);
454 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
455 sal_Bool
bShowImages( rSettings
.GetUseImagesInMenus() );
457 PopupMenu
* pVCLPopupMenu
= (PopupMenu
*)pPopupMenu
->GetMenu();
459 if ( m_bShowImages
!= bShowImages
)
461 m_bShowImages
= bShowImages
;
462 setMenuImages( pVCLPopupMenu
, m_bShowImages
);
465 setAccelerators( pVCLPopupMenu
);
470 // XPopupMenuController
471 void NewMenuController::impl_setPopupMenu()
474 if ( m_xPopupMenu
.is() )
475 fillPopupMenu( m_xPopupMenu
);
477 // Identify module that we are attach to. It's our context that we need to know.
478 Reference
< XModuleManager2
> xModuleManager
= ModuleManager::create( comphelper::getComponentContext(m_xServiceManager
) );
481 m_aModuleIdentifier
= xModuleManager
->identify( m_xFrame
);
482 m_bModuleIdentified
= sal_True
;
484 if ( !m_aModuleIdentifier
.isEmpty() )
486 Sequence
< PropertyValue
> aSeq
;
488 if ( xModuleManager
->getByName( m_aModuleIdentifier
) >>= aSeq
)
490 for ( sal_Int32 y
= 0; y
< aSeq
.getLength(); y
++ )
492 if ( aSeq
[y
].Name
== "ooSetupFactoryEmptyDocumentURL" )
494 aSeq
[y
].Value
>>= m_aEmptyDocURL
;
501 catch ( const RuntimeException
& )
505 catch ( const Exception
& )
511 void SAL_CALL
NewMenuController::initialize( const Sequence
< Any
>& aArguments
) throw ( Exception
, RuntimeException
)
513 osl::MutexGuard
aLock( m_aMutex
);
515 sal_Bool
bInitalized( m_bInitialized
);
518 svt::PopupMenuControllerBase::initialize( aArguments
);
520 if ( m_bInitialized
)
522 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
524 m_bShowImages
= rSettings
.GetUseImagesInMenus();
525 m_bNewMenu
= m_aCommandURL
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ".uno:AddDirect" ) );
530 IMPL_STATIC_LINK_NOINSTANCE( NewMenuController
, ExecuteHdl_Impl
, NewDocument
*, pNewDocument
)
532 /* i62706: Don't catch all exceptions. We hide all problems here and are not able
533 to handle them on higher levels.
537 // Asynchronous execution as this can lead to our own destruction!
538 // Framework can recycle our current frame and the layout manager disposes all user interface
539 // elements if a component gets detached from its frame!
540 pNewDocument
->xDispatch
->dispatch( pNewDocument
->aTargetURL
, pNewDocument
->aArgSeq
);
547 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */