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 <helper/persistentwindowstate.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/settings.hxx>
38 #include <vcl/commandinfoprovider.hxx>
39 #include <svtools/acceleratorexecute.hxx>
40 #include <svtools/imagemgr.hxx>
41 #include <toolkit/awt/vclxmenu.hxx>
42 #include <tools/urlobj.hxx>
43 #include <unotools/dynamicmenuoptions.hxx>
44 #include <osl/mutex.hxx>
45 #include <cppuhelper/supportsservice.hxx>
48 constexpr OUString aSlotNewDocDirect
= u
".uno:AddDirect"_ustr
;
49 constexpr OUString aSlotAutoPilot
= u
".uno:AutoPilotMenu"_ustr
;
51 using namespace com::sun::star::uno
;
52 using namespace com::sun::star::lang
;
53 using namespace com::sun::star::frame
;
54 using namespace com::sun::star::beans
;
55 using namespace com::sun::star::util
;
56 using namespace com::sun::star::ui
;
61 * A simple status listener, storing the "enabled" status from the last status notification
63 class SlotStatusGetter
: public comphelper::WeakImplHelper
<css::frame::XStatusListener
>
66 bool isEnabled() const { return m_bEnabled
; }
70 void SAL_CALL
statusChanged(const css::frame::FeatureStateEvent
& state
) override
72 m_bEnabled
= state
.IsEnabled
;
76 void SAL_CALL
disposing(const css::lang::EventObject
&) override
{} // unused
78 bool m_bEnabled
= false;
81 bool isSlotActive(const OUString
& slot
, const css::uno::Reference
<css::frame::XFrame
>& frame
,
82 const css::uno::Reference
<css::util::XURLTransformer
>& transformer
)
84 if (auto provider
= frame
.query
<css::frame::XDispatchProvider
>())
88 transformer
->parseStrict(url
);
89 if (auto dispatch
= provider
->queryDispatch(url
, {}, 0))
91 rtl::Reference
slotStatus(new SlotStatusGetter
);
92 // Adding as listener will automatically emit an initial notification. The status
93 // reported in the notification will be stored in the SlotStatusGetter instance.
94 dispatch
->addStatusListener(slotStatus
, url
);
95 dispatch
->removeStatusListener(slotStatus
, url
);
96 return slotStatus
->isEnabled();
107 OUString SAL_CALL
NewMenuController::getImplementationName()
109 return u
"com.sun.star.comp.framework.NewMenuController"_ustr
;
112 sal_Bool SAL_CALL
NewMenuController::supportsService( const OUString
& sServiceName
)
114 return cppu::supportsService(this, sServiceName
);
117 css::uno::Sequence
< OUString
> SAL_CALL
NewMenuController::getSupportedServiceNames()
119 return { SERVICENAME_POPUPMENUCONTROLLER
};
122 void NewMenuController::setMenuImages( PopupMenu
* pPopupMenu
, bool bSetImages
)
124 sal_uInt16 nItemCount
= pPopupMenu
->GetItemCount();
125 Reference
< XFrame
> xFrame( m_xFrame
);
127 for ( sal_uInt16 i
= 0; i
< nItemCount
; i
++ )
129 sal_uInt16 nItemId
= pPopupMenu
->GetItemId( i
);
135 OUString
aCmd( pPopupMenu
->GetItemCommand( nItemId
) );
136 void* nAttributePtr
= pPopupMenu
->GetUserValue( nItemId
);
137 MenuAttributes
* pAttributes
= static_cast<MenuAttributes
*>(nAttributePtr
);
139 aImageId
= pAttributes
->aImageId
;
141 INetURLObject
aURLObj( aImageId
.isEmpty() ? aCmd
: aImageId
);
142 Image aImage
= SvFileInformationManager::GetImageNoDefault( aURLObj
);
144 aImage
= vcl::CommandInfoProvider::GetImageForCommand(aCmd
, xFrame
);
147 pPopupMenu
->SetItemImage( nItemId
, aImage
);
150 pPopupMenu
->SetItemImage( nItemId
, Image() );
155 void NewMenuController::determineAndSetNewDocAccel(const css::awt::KeyEvent
& rKeyCode
)
157 sal_uInt16
nCount(m_xPopupMenu
->getItemCount());
161 if ( !m_aEmptyDocURL
.isEmpty() )
163 // Search for the empty document URL
165 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
167 if (m_xPopupMenu
->getItemType(i
) != css::awt::MenuItemType_SEPARATOR
)
169 nId
= m_xPopupMenu
->getItemId(i
);
170 aCommand
= m_xPopupMenu
->getCommand(nId
);
171 if ( aCommand
.startsWith( m_aEmptyDocURL
) )
173 m_xPopupMenu
->setAcceleratorKeyEvent(nId
, rKeyCode
);
181 void NewMenuController::setAccelerators()
183 if ( !m_bModuleIdentified
)
186 Reference
< XAcceleratorConfiguration
> xDocAccelCfg( m_xDocAcceleratorManager
);
187 Reference
< XAcceleratorConfiguration
> xModuleAccelCfg( m_xModuleAcceleratorManager
);
188 Reference
< XAcceleratorConfiguration
> xGlobalAccelCfg( m_xGlobalAcceleratorManager
);
190 if ( !m_bAcceleratorCfg
)
192 // Retrieve references on demand
193 m_bAcceleratorCfg
= true;
194 if ( !xDocAccelCfg
.is() )
196 Reference
< XController
> xController
= m_xFrame
->getController();
197 Reference
< XModel
> xModel
;
198 if ( xController
.is() )
200 xModel
= xController
->getModel();
203 Reference
< XUIConfigurationManagerSupplier
> xSupplier( xModel
, UNO_QUERY
);
204 if ( xSupplier
.is() )
206 Reference
< XUIConfigurationManager
> xDocUICfgMgr
= xSupplier
->getUIConfigurationManager();
207 if ( xDocUICfgMgr
.is() )
209 xDocAccelCfg
= xDocUICfgMgr
->getShortCutManager();
210 m_xDocAcceleratorManager
= xDocAccelCfg
;
217 if ( !xModuleAccelCfg
.is() )
219 Reference
< XModuleUIConfigurationManagerSupplier
> xModuleCfgMgrSupplier
=
220 theModuleUIConfigurationManagerSupplier::get( m_xContext
);
221 Reference
< XUIConfigurationManager
> xUICfgMgr
= xModuleCfgMgrSupplier
->getUIConfigurationManager( m_aModuleIdentifier
);
222 if ( xUICfgMgr
.is() )
224 xModuleAccelCfg
= xUICfgMgr
->getShortCutManager();
225 m_xModuleAcceleratorManager
= xModuleAccelCfg
;
229 if ( !xGlobalAccelCfg
.is() )
231 xGlobalAccelCfg
= GlobalAcceleratorConfiguration::create( m_xContext
);
232 m_xGlobalAcceleratorManager
= xGlobalAccelCfg
;
236 vcl::KeyCode aEmptyKeyCode
;
237 sal_uInt16
nItemCount(m_xPopupMenu
->getItemCount());
238 std::vector
< vcl::KeyCode
> aMenuShortCuts
;
239 std::vector
< OUString
> aCmds
;
240 std::vector
< sal_uInt16
> aIds
;
241 for ( sal_uInt16 i
= 0; i
< nItemCount
; i
++ )
243 if (m_xPopupMenu
->getItemType(i
) != css::awt::MenuItemType_SEPARATOR
)
245 sal_uInt16
nId(m_xPopupMenu
->getItemId(i
));
246 aIds
.push_back( nId
);
247 aMenuShortCuts
.push_back( aEmptyKeyCode
);
248 aCmds
.push_back(m_xPopupMenu
->getCommand(nId
));
252 sal_uInt32
nSeqCount( aIds
.size() );
257 Sequence
< OUString
> aSeq( nSeqCount
);
258 auto aSeqRange
= asNonConstRange(aSeq
);
260 // Add a special command for our "New" menu.
263 aSeqRange
[nSeqCount
-1] = m_aCommandURL
;
264 aMenuShortCuts
.push_back( aEmptyKeyCode
);
267 const sal_uInt32 nCount
= aCmds
.size();
268 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
269 aSeqRange
[i
] = aCmds
[i
];
271 if ( m_xGlobalAcceleratorManager
.is() )
272 retrieveShortcutsFromConfiguration( xGlobalAccelCfg
, aSeq
, aMenuShortCuts
);
273 if ( m_xModuleAcceleratorManager
.is() )
274 retrieveShortcutsFromConfiguration( xModuleAccelCfg
, aSeq
, aMenuShortCuts
);
275 if ( m_xDocAcceleratorManager
.is() )
276 retrieveShortcutsFromConfiguration( xDocAccelCfg
, aSeq
, aMenuShortCuts
);
278 const sal_uInt32 nCount2
= aIds
.size();
279 for ( sal_uInt32 i
= 0; i
< nCount2
; i
++ )
280 m_xPopupMenu
->setAcceleratorKeyEvent(aIds
[i
], svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts
[i
]));
282 // Special handling for "New" menu short-cut should be set at the
283 // document which will be opened using it.
286 if ( aMenuShortCuts
[nSeqCount
-1] != aEmptyKeyCode
)
287 determineAndSetNewDocAccel(svt::AcceleratorExecute::st_VCLKey2AWTKey(aMenuShortCuts
[nSeqCount
-1]));
292 void NewMenuController::retrieveShortcutsFromConfiguration(
293 const Reference
< XAcceleratorConfiguration
>& rAccelCfg
,
294 const Sequence
< OUString
>& rCommands
,
295 std::vector
< vcl::KeyCode
>& aMenuShortCuts
)
297 if ( !rAccelCfg
.is() )
302 css::awt::KeyEvent aKeyEvent
;
303 Sequence
< Any
> aSeqKeyCode
= rAccelCfg
->getPreferredKeyEventsForCommandList( rCommands
);
304 for ( sal_Int32 i
= 0; i
< aSeqKeyCode
.getLength(); i
++ )
306 if ( aSeqKeyCode
[i
] >>= aKeyEvent
)
307 aMenuShortCuts
[i
] = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent
);
310 catch ( const IllegalArgumentException
& )
315 NewMenuController::NewMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
) :
316 svt::PopupMenuControllerBase( xContext
),
317 m_bShowImages( true ),
319 m_bModuleIdentified( false ),
320 m_bAcceleratorCfg( false ),
321 m_aTargetFrame( u
"_default"_ustr
),
322 m_xContext( xContext
)
326 NewMenuController::~NewMenuController()
331 void NewMenuController::fillPopupMenu( Reference
< css::awt::XPopupMenu
> const & rPopupMenu
)
333 VCLXPopupMenu
* pPopupMenu
= static_cast<VCLXPopupMenu
*>(dynamic_cast<VCLXMenu
*>( rPopupMenu
.get() ));
334 PopupMenu
* pVCLPopupMenu
= nullptr;
336 SolarMutexGuard aSolarMutexGuard
;
338 resetPopupMenu( rPopupMenu
);
340 pVCLPopupMenu
= static_cast<PopupMenu
*>(pPopupMenu
->GetMenu());
342 if ( !pVCLPopupMenu
)
345 if (!isSlotActive(m_bNewMenu
? aSlotNewDocDirect
: aSlotAutoPilot
, m_xFrame
, m_xURLTransformer
))
348 const std::vector
< SvtDynMenuEntry
> aDynamicMenuEntries
=
349 SvtDynamicMenuOptions::GetMenu( m_bNewMenu
? EDynamicMenuType::NewMenu
: EDynamicMenuType::WizardMenu
);
351 sal_uInt16 nItemId
= 1;
353 for ( const auto& aDynamicMenuEntry
: aDynamicMenuEntries
)
355 if ( aDynamicMenuEntry
.sTitle
.isEmpty() && aDynamicMenuEntry
.sURL
.isEmpty() )
358 if ( aDynamicMenuEntry
.sURL
== "private:separator" )
359 rPopupMenu
->insertSeparator(-1);
362 rPopupMenu
->insertItem(nItemId
, aDynamicMenuEntry
.sTitle
, 0, -1);
363 rPopupMenu
->setCommand(nItemId
, aDynamicMenuEntry
.sURL
);
365 void* nAttributePtr
= MenuAttributes::CreateAttribute( aDynamicMenuEntry
.sTargetName
, aDynamicMenuEntry
.sImageIdentifier
);
366 pPopupMenu
->setUserValue(nItemId
, nAttributePtr
, MenuAttributes::ReleaseAttribute
);
373 setMenuImages( pVCLPopupMenu
, m_bShowImages
);
377 void SAL_CALL
NewMenuController::disposing( const EventObject
& )
379 Reference
< css::awt::XMenuListener
> xHolder(this);
381 std::unique_lock
aLock( m_aMutex
);
386 if ( m_xPopupMenu
.is() )
387 m_xPopupMenu
->removeMenuListener( Reference
< css::awt::XMenuListener
>(this) );
388 m_xPopupMenu
.clear();
392 void SAL_CALL
NewMenuController::statusChanged( const FeatureStateEvent
& Event
)
394 Event
.State
>>= m_aEmptyDocURL
;
398 void SAL_CALL
NewMenuController::itemSelected( const css::awt::MenuEvent
& rEvent
)
400 Reference
< css::awt::XPopupMenu
> xPopupMenu
;
401 Reference
< XComponentContext
> xContext
;
404 std::unique_lock
aLock(m_aMutex
);
405 xPopupMenu
= m_xPopupMenu
;
406 xContext
= m_xContext
;
409 if ( !xPopupMenu
.is() )
412 VCLXPopupMenu
* pPopupMenu
= static_cast<VCLXPopupMenu
*>(dynamic_cast<VCLXMenu
*>( xPopupMenu
.get() ));
417 OUString
aTargetFrame( m_aTargetFrame
);
420 SolarMutexGuard aSolarMutexGuard
;
421 aURL
= pPopupMenu
->getCommand(rEvent
.MenuId
);
422 void* nAttributePtr
= pPopupMenu
->getUserValue(rEvent
.MenuId
);
423 MenuAttributes
* pAttributes
= static_cast<MenuAttributes
*>(nAttributePtr
);
425 aTargetFrame
= pAttributes
->aTargetFrame
;
428 // tdf#144407 save the current window state so a new window of the same type will
429 // open with the same settings
430 PersistentWindowState::SaveWindowStateToConfig(m_xContext
, m_xFrame
);
432 Sequence
< PropertyValue
> aArgsList
{ comphelper::makePropertyValue(u
"Referer"_ustr
,
433 u
"private:user"_ustr
) };
435 dispatchCommand( aURL
, aArgsList
, aTargetFrame
);
438 void SAL_CALL
NewMenuController::itemActivated( const css::awt::MenuEvent
& )
440 SolarMutexGuard aSolarMutexGuard
;
441 if ( !(m_xFrame
.is() && m_xPopupMenu
.is()) )
444 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
445 bool bShowImages( rSettings
.GetUseImagesInMenus() );
446 OUString
aIconTheme( rSettings
.DetermineIconTheme() );
448 PopupMenu
* pVCLPopupMenu
= static_cast<PopupMenu
*>(m_xPopupMenu
->GetMenu());
450 if ( m_bShowImages
!= bShowImages
|| m_aIconTheme
!= aIconTheme
)
452 m_bShowImages
= bShowImages
;
453 m_aIconTheme
= aIconTheme
;
454 setMenuImages( pVCLPopupMenu
, m_bShowImages
);
460 // XPopupMenuController
461 void NewMenuController::impl_setPopupMenu(std::unique_lock
<std::mutex
>& /*rGuard*/)
464 if ( m_xPopupMenu
.is() )
465 fillPopupMenu( m_xPopupMenu
);
467 // Identify module that we are attach to. It's our context that we need to know.
468 Reference
< XModuleManager2
> xModuleManager
= ModuleManager::create( m_xContext
);
471 m_aModuleIdentifier
= xModuleManager
->identify( m_xFrame
);
472 m_bModuleIdentified
= true;
474 catch ( const RuntimeException
& )
478 catch ( const Exception
& )
484 void NewMenuController::initializeImpl( std::unique_lock
<std::mutex
>& rGuard
, const Sequence
< Any
>& aArguments
)
486 bool bInitialized( m_bInitialized
);
490 svt::PopupMenuControllerBase::initializeImpl( rGuard
, aArguments
);
492 if ( m_bInitialized
)
494 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
496 m_bShowImages
= rSettings
.GetUseImagesInMenus();
497 m_aIconTheme
= rSettings
.DetermineIconTheme();
498 m_bNewMenu
= m_aCommandURL
== aSlotNewDocDirect
;
505 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
506 framework_NewMenuController_get_implementation(
507 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
509 return cppu::acquire(new framework::NewMenuController(context
));
512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */