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>
21 #include <menuconfiguration.hxx>
25 #include <com/sun/star/awt/MenuItemType.hpp>
26 #include <com/sun/star/beans/PropertyValue.hpp>
27 #include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
28 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
29 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
30 #include <com/sun/star/frame/ModuleManager.hpp>
31 #include <com/sun/star/frame/XFrame.hpp>
32 #include <com/sun/star/util/XURLTransformer.hpp>
34 #include <comphelper/propertyvalue.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/commandinfoprovider.hxx>
38 #include <svtools/acceleratorexecute.hxx>
39 #include <svtools/imagemgr.hxx>
40 #include <toolkit/awt/vclxmenu.hxx>
41 #include <tools/urlobj.hxx>
42 #include <unotools/dynamicmenuoptions.hxx>
43 #include <osl/mutex.hxx>
44 #include <cppuhelper/supportsservice.hxx>
47 constexpr OUStringLiteral aSlotNewDocDirect
= u
".uno:AddDirect";
48 constexpr OUStringLiteral aSlotAutoPilot
= u
".uno:AutoPilotMenu";
50 using namespace com::sun::star::uno
;
51 using namespace com::sun::star::lang
;
52 using namespace com::sun::star::frame
;
53 using namespace com::sun::star::beans
;
54 using namespace com::sun::star::util
;
55 using namespace com::sun::star::container
;
56 using namespace com::sun::star::ui
;
61 OUString SAL_CALL
NewMenuController::getImplementationName()
63 return "com.sun.star.comp.framework.NewMenuController";
66 sal_Bool SAL_CALL
NewMenuController::supportsService( const OUString
& sServiceName
)
68 return cppu::supportsService(this, sServiceName
);
71 css::uno::Sequence
< OUString
> SAL_CALL
NewMenuController::getSupportedServiceNames()
73 return { SERVICENAME_POPUPMENUCONTROLLER
};
76 void NewMenuController::setMenuImages( PopupMenu
* pPopupMenu
, bool bSetImages
)
78 sal_uInt16 nItemCount
= pPopupMenu
->GetItemCount();
79 Reference
< XFrame
> xFrame( m_xFrame
);
81 for ( sal_uInt16 i
= 0; i
< nItemCount
; i
++ )
83 sal_uInt16 nItemId
= pPopupMenu
->GetItemId( i
);
89 OUString
aCmd( pPopupMenu
->GetItemCommand( nItemId
) );
90 void* nAttributePtr
= pPopupMenu
->GetUserValue( nItemId
);
91 MenuAttributes
* pAttributes
= static_cast<MenuAttributes
*>(nAttributePtr
);
93 aImageId
= pAttributes
->aImageId
;
95 INetURLObject
aURLObj( aImageId
.isEmpty() ? aCmd
: aImageId
);
96 Image aImage
= SvFileInformationManager::GetImageNoDefault( aURLObj
);
98 aImage
= vcl::CommandInfoProvider::GetImageForCommand(aCmd
, xFrame
);
101 pPopupMenu
->SetItemImage( nItemId
, aImage
);
104 pPopupMenu
->SetItemImage( nItemId
, Image() );
109 void NewMenuController::determineAndSetNewDocAccel(const css::awt::KeyEvent
& rKeyCode
)
111 sal_uInt16
nCount(m_xPopupMenu
->getItemCount());
115 if ( !m_aEmptyDocURL
.isEmpty() )
117 // Search for the empty document URL
119 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
121 if (m_xPopupMenu
->getItemType(i
) != css::awt::MenuItemType_SEPARATOR
)
123 nId
= m_xPopupMenu
->getItemId(i
);
124 aCommand
= m_xPopupMenu
->getCommand(nId
);
125 if ( aCommand
.startsWith( m_aEmptyDocURL
) )
127 m_xPopupMenu
->setAcceleratorKeyEvent(nId
, rKeyCode
);
135 void NewMenuController::setAccelerators()
137 if ( !m_bModuleIdentified
)
140 Reference
< XAcceleratorConfiguration
> xDocAccelCfg( m_xDocAcceleratorManager
);
141 Reference
< XAcceleratorConfiguration
> xModuleAccelCfg( m_xModuleAcceleratorManager
);
142 Reference
< XAcceleratorConfiguration
> xGlobalAccelCfg( m_xGlobalAcceleratorManager
);
144 if ( !m_bAcceleratorCfg
)
146 // Retrieve references on demand
147 m_bAcceleratorCfg
= true;
148 if ( !xDocAccelCfg
.is() )
150 Reference
< XController
> xController
= m_xFrame
->getController();
151 Reference
< XModel
> xModel
;
152 if ( xController
.is() )
154 xModel
= xController
->getModel();
157 Reference
< XUIConfigurationManagerSupplier
> xSupplier( xModel
, UNO_QUERY
);
158 if ( xSupplier
.is() )
160 Reference
< XUIConfigurationManager
> xDocUICfgMgr
= xSupplier
->getUIConfigurationManager();
161 if ( xDocUICfgMgr
.is() )
163 xDocAccelCfg
= xDocUICfgMgr
->getShortCutManager();
164 m_xDocAcceleratorManager
= xDocAccelCfg
;
171 if ( !xModuleAccelCfg
.is() )
173 Reference
< XModuleUIConfigurationManagerSupplier
> xModuleCfgMgrSupplier
=
174 theModuleUIConfigurationManagerSupplier::get( m_xContext
);
175 Reference
< XUIConfigurationManager
> xUICfgMgr
= xModuleCfgMgrSupplier
->getUIConfigurationManager( m_aModuleIdentifier
);
176 if ( xUICfgMgr
.is() )
178 xModuleAccelCfg
= xUICfgMgr
->getShortCutManager();
179 m_xModuleAcceleratorManager
= xModuleAccelCfg
;
183 if ( !xGlobalAccelCfg
.is() )
185 xGlobalAccelCfg
= GlobalAcceleratorConfiguration::create( m_xContext
);
186 m_xGlobalAcceleratorManager
= xGlobalAccelCfg
;
190 vcl::KeyCode aEmptyKeyCode
;
191 sal_uInt16
nItemCount(m_xPopupMenu
->getItemCount());
192 std::vector
< vcl::KeyCode
> aMenuShortCuts
;
193 std::vector
< OUString
> aCmds
;
194 std::vector
< sal_uInt16
> aIds
;
195 for ( sal_uInt16 i
= 0; i
< nItemCount
; i
++ )
197 if (m_xPopupMenu
->getItemType(i
) != css::awt::MenuItemType_SEPARATOR
)
199 sal_uInt16
nId(m_xPopupMenu
->getItemId(i
));
200 aIds
.push_back( nId
);
201 aMenuShortCuts
.push_back( aEmptyKeyCode
);
202 aCmds
.push_back(m_xPopupMenu
->getCommand(nId
));
206 sal_uInt32
nSeqCount( aIds
.size() );
211 Sequence
< OUString
> aSeq( nSeqCount
);
212 auto aSeqRange
= asNonConstRange(aSeq
);
214 // Add a special command for our "New" menu.
217 aSeqRange
[nSeqCount
-1] = m_aCommandURL
;
218 aMenuShortCuts
.push_back( aEmptyKeyCode
);
221 const sal_uInt32 nCount
= aCmds
.size();
222 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
223 aSeqRange
[i
] = aCmds
[i
];
225 if ( m_xGlobalAcceleratorManager
.is() )
226 retrieveShortcutsFromConfiguration( xGlobalAccelCfg
, aSeq
, aMenuShortCuts
);
227 if ( m_xModuleAcceleratorManager
.is() )
228 retrieveShortcutsFromConfiguration( xModuleAccelCfg
, aSeq
, aMenuShortCuts
);
229 if ( m_xDocAcceleratorManager
.is() )
230 retrieveShortcutsFromConfiguration( xDocAccelCfg
, aSeq
, aMenuShortCuts
);
232 const sal_uInt32 nCount2
= aIds
.size();
233 for ( sal_uInt32 i
= 0; i
< nCount2
; i
++ )
234 m_xPopupMenu
->setAcceleratorKeyEvent(aIds
[i
], svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts
[i
]));
236 // Special handling for "New" menu short-cut should be set at the
237 // document which will be opened using it.
240 if ( aMenuShortCuts
[nSeqCount
-1] != aEmptyKeyCode
)
241 determineAndSetNewDocAccel(svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts
[nSeqCount
-1]));
245 void NewMenuController::retrieveShortcutsFromConfiguration(
246 const Reference
< XAcceleratorConfiguration
>& rAccelCfg
,
247 const Sequence
< OUString
>& rCommands
,
248 std::vector
< vcl::KeyCode
>& aMenuShortCuts
)
250 if ( !rAccelCfg
.is() )
255 css::awt::KeyEvent aKeyEvent
;
256 Sequence
< Any
> aSeqKeyCode
= rAccelCfg
->getPreferredKeyEventsForCommandList( rCommands
);
257 for ( sal_Int32 i
= 0; i
< aSeqKeyCode
.getLength(); i
++ )
259 if ( aSeqKeyCode
[i
] >>= aKeyEvent
)
260 aMenuShortCuts
[i
] = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent
);
263 catch ( const IllegalArgumentException
& )
268 NewMenuController::NewMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
) :
269 svt::PopupMenuControllerBase( xContext
),
270 m_bShowImages( true ),
272 m_bModuleIdentified( false ),
273 m_bAcceleratorCfg( false ),
274 m_aTargetFrame( "_default" ),
275 m_xContext( xContext
)
279 NewMenuController::~NewMenuController()
284 void NewMenuController::fillPopupMenu( Reference
< css::awt::XPopupMenu
> const & rPopupMenu
)
286 VCLXPopupMenu
* pPopupMenu
= static_cast<VCLXPopupMenu
*>(dynamic_cast<VCLXMenu
*>( rPopupMenu
.get() ));
287 PopupMenu
* pVCLPopupMenu
= nullptr;
289 SolarMutexGuard aSolarMutexGuard
;
291 resetPopupMenu( rPopupMenu
);
293 pVCLPopupMenu
= static_cast<PopupMenu
*>(pPopupMenu
->GetMenu());
295 if ( !pVCLPopupMenu
)
298 Reference
< XDispatchProvider
> xDispatchProvider( m_xFrame
, UNO_QUERY
);
300 aTargetURL
.Complete
= m_bNewMenu
? OUString(aSlotNewDocDirect
) : OUString(aSlotAutoPilot
);
301 m_xURLTransformer
->parseStrict( aTargetURL
);
302 Reference
< XDispatch
> xMenuItemDispatch
= xDispatchProvider
->queryDispatch( aTargetURL
, OUString(), 0 );
303 if(xMenuItemDispatch
== nullptr)
306 const std::vector
< SvtDynMenuEntry
> aDynamicMenuEntries
=
307 SvtDynamicMenuOptions::GetMenu( m_bNewMenu
? EDynamicMenuType::NewMenu
: EDynamicMenuType::WizardMenu
);
309 sal_uInt16 nItemId
= 1;
311 for ( const auto& aDynamicMenuEntry
: aDynamicMenuEntries
)
313 if ( aDynamicMenuEntry
.sTitle
.isEmpty() && aDynamicMenuEntry
.sURL
.isEmpty() )
316 if ( aDynamicMenuEntry
.sURL
== "private:separator" )
317 rPopupMenu
->insertSeparator(-1);
320 rPopupMenu
->insertItem(nItemId
, aDynamicMenuEntry
.sTitle
, 0, -1);
321 rPopupMenu
->setCommand(nItemId
, aDynamicMenuEntry
.sURL
);
323 void* nAttributePtr
= MenuAttributes::CreateAttribute( aDynamicMenuEntry
.sTargetName
, aDynamicMenuEntry
.sImageIdentifier
);
324 pPopupMenu
->setUserValue(nItemId
, nAttributePtr
, MenuAttributes::ReleaseAttribute
);
331 setMenuImages( pVCLPopupMenu
, m_bShowImages
);
335 void SAL_CALL
NewMenuController::disposing( const EventObject
& )
337 Reference
< css::awt::XMenuListener
> xHolder(this);
339 std::unique_lock
aLock( m_aMutex
);
344 if ( m_xPopupMenu
.is() )
345 m_xPopupMenu
->removeMenuListener( Reference
< css::awt::XMenuListener
>(this) );
346 m_xPopupMenu
.clear();
350 void SAL_CALL
NewMenuController::statusChanged( const FeatureStateEvent
& Event
)
352 Event
.State
>>= m_aEmptyDocURL
;
356 void SAL_CALL
NewMenuController::itemSelected( const css::awt::MenuEvent
& rEvent
)
358 Reference
< css::awt::XPopupMenu
> xPopupMenu
;
359 Reference
< XComponentContext
> xContext
;
362 std::unique_lock
aLock(m_aMutex
);
363 xPopupMenu
= m_xPopupMenu
;
364 xContext
= m_xContext
;
367 if ( !xPopupMenu
.is() )
370 VCLXPopupMenu
* pPopupMenu
= static_cast<VCLXPopupMenu
*>(dynamic_cast<VCLXMenu
*>( xPopupMenu
.get() ));
375 OUString
aTargetFrame( m_aTargetFrame
);
378 SolarMutexGuard aSolarMutexGuard
;
379 aURL
= pPopupMenu
->getCommand(rEvent
.MenuId
);
380 void* nAttributePtr
= pPopupMenu
->getUserValue(rEvent
.MenuId
);
381 MenuAttributes
* pAttributes
= static_cast<MenuAttributes
*>(nAttributePtr
);
383 aTargetFrame
= pAttributes
->aTargetFrame
;
386 Sequence
< PropertyValue
> aArgsList
{ comphelper::makePropertyValue("Referer",
387 OUString( "private:user" )) };
389 dispatchCommand( aURL
, aArgsList
, aTargetFrame
);
392 void SAL_CALL
NewMenuController::itemActivated( const css::awt::MenuEvent
& )
394 SolarMutexGuard aSolarMutexGuard
;
395 if ( !(m_xFrame
.is() && m_xPopupMenu
.is()) )
398 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
399 bool bShowImages( rSettings
.GetUseImagesInMenus() );
400 OUString
aIconTheme( rSettings
.DetermineIconTheme() );
402 PopupMenu
* pVCLPopupMenu
= static_cast<PopupMenu
*>(m_xPopupMenu
->GetMenu());
404 if ( m_bShowImages
!= bShowImages
|| m_aIconTheme
!= aIconTheme
)
406 m_bShowImages
= bShowImages
;
407 m_aIconTheme
= aIconTheme
;
408 setMenuImages( pVCLPopupMenu
, m_bShowImages
);
414 // XPopupMenuController
415 void NewMenuController::impl_setPopupMenu()
418 if ( m_xPopupMenu
.is() )
419 fillPopupMenu( m_xPopupMenu
);
421 // Identify module that we are attach to. It's our context that we need to know.
422 Reference
< XModuleManager2
> xModuleManager
= ModuleManager::create( m_xContext
);
425 m_aModuleIdentifier
= xModuleManager
->identify( m_xFrame
);
426 m_bModuleIdentified
= true;
428 catch ( const RuntimeException
& )
432 catch ( const Exception
& )
438 void NewMenuController::initializeImpl( std::unique_lock
<std::mutex
>& rGuard
, const Sequence
< Any
>& aArguments
)
440 bool bInitialized( m_bInitialized
);
444 svt::PopupMenuControllerBase::initializeImpl( rGuard
, aArguments
);
446 if ( m_bInitialized
)
448 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
450 m_bShowImages
= rSettings
.GetUseImagesInMenus();
451 m_aIconTheme
= rSettings
.DetermineIconTheme();
452 m_bNewMenu
= m_aCommandURL
== aSlotNewDocDirect
;
459 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
460 framework_NewMenuController_get_implementation(
461 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
463 return cppu::acquire(new framework::NewMenuController(context
));
466 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */