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/menubarmanager.hxx>
22 #include <cppuhelper/implbase.hxx>
23 #include <svtools/popupmenucontrollerbase.hxx>
24 #include <toolkit/awt/vclxmenu.hxx>
25 #include <toolkit/helper/vclunohelper.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/window.hxx>
28 #include <sal/log.hxx>
30 #include <com/sun/star/embed/VerbAttributes.hpp>
31 #include <com/sun/star/embed/VerbDescriptor.hpp>
32 #include <com/sun/star/frame/Desktop.hpp>
33 #include <com/sun/star/frame/ModuleManager.hpp>
34 #include <com/sun/star/frame/XStorable.hpp>
35 #include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
36 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
37 #include <com/sun/star/util/URL.hpp>
41 class ResourceMenuController
: public cppu::ImplInheritanceHelper
< svt::PopupMenuControllerBase
, css::ui::XUIConfigurationListener
>
44 ResourceMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rxContext
,
45 const css::uno::Sequence
< css::uno::Any
>& rxArgs
, bool bToolbarContainer
);
47 // XPopupMenuController
48 virtual void SAL_CALL
updatePopupMenu() override
;
51 virtual void SAL_CALL
statusChanged( const css::frame::FeatureStateEvent
& rEvent
) override
;
54 virtual void SAL_CALL
disposing( const css::lang::EventObject
& rEvent
) override
;
56 // XUIConfigurationListener
57 virtual void SAL_CALL
elementInserted( const css::ui::ConfigurationEvent
& rEvent
) override
;
58 virtual void SAL_CALL
elementRemoved( const css::ui::ConfigurationEvent
& rEvent
) override
;
59 virtual void SAL_CALL
elementReplaced( const css::ui::ConfigurationEvent
& rEvent
) override
;
62 virtual void SAL_CALL
itemActivated( const css::awt::MenuEvent
& rEvent
) override
;
63 virtual void SAL_CALL
itemSelected( const css::awt::MenuEvent
& rEvent
) override
;
66 virtual OUString SAL_CALL
getImplementationName() override
;
67 virtual css::uno::Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
73 bool m_bToolbarContainer
;
74 sal_uInt16 m_nNewMenuId
;
75 rtl::Reference
< framework::MenuBarManager
> m_xMenuBarManager
;
76 css::uno::Reference
< css::frame::XDispatchProvider
> m_xDispatchProvider
;
77 css::uno::Reference
< css::container::XIndexAccess
> m_xMenuContainer
;
78 css::uno::Reference
< css::ui::XUIConfigurationManager
> m_xConfigManager
, m_xModuleConfigManager
;
79 void addVerbs( const css::uno::Sequence
< css::embed::VerbDescriptor
>& rVerbs
);
80 virtual void disposing(std::unique_lock
<std::mutex
>& rGuard
) override
;
83 css::uno::Reference
< css::uno::XComponentContext
> m_xContext
;
86 ResourceMenuController::ResourceMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rxContext
,
87 const css::uno::Sequence
< css::uno::Any
>& rxArgs
, bool bToolbarContainer
) :
88 ImplInheritanceHelper( rxContext
),
89 m_bContextMenu( false ),
90 m_bInToolbar( false ),
91 m_bToolbarContainer( bToolbarContainer
),
93 m_xContext( rxContext
)
95 for ( const auto& arg
: rxArgs
)
97 css::beans::PropertyValue aPropValue
;
98 if ( arg
>>= aPropValue
)
100 if ( aPropValue
.Name
== "Value" )
103 aPropValue
.Value
>>= aMenuName
;
104 if ( aMenuName
.isEmpty() )
107 if ( m_bToolbarContainer
)
108 m_aMenuURL
= "private:resource/toolbar/" + aMenuName
;
110 m_aMenuURL
= "private:resource/popupmenu/" + aMenuName
;
112 else if ( aPropValue
.Name
== "ResourceURL" )
113 aPropValue
.Value
>>= m_aMenuURL
;
114 else if ( aPropValue
.Name
== "Frame" )
115 aPropValue
.Value
>>= m_xFrame
;
116 else if ( aPropValue
.Name
== "ModuleIdentifier" )
117 aPropValue
.Value
>>= m_aModuleName
;
118 else if ( aPropValue
.Name
== "DispatchProvider" )
119 aPropValue
.Value
>>= m_xDispatchProvider
;
120 else if ( aPropValue
.Name
== "IsContextMenu" )
121 aPropValue
.Value
>>= m_bContextMenu
;
122 else if ( aPropValue
.Name
== "InToolbar" )
123 aPropValue
.Value
>>= m_bInToolbar
;
127 // No need to initialize again through initialize method.
128 m_bInitialized
= true;
131 void ResourceMenuController::updatePopupMenu()
133 if ( ( m_xMenuContainer
.is() && !m_bContextMenu
) || m_aMenuURL
.isEmpty() )
136 if ( m_aModuleName
.isEmpty() )
140 css::uno::Reference
< css::frame::XModuleManager
> xModuleManager( css::frame::ModuleManager::create( m_xContext
) );
141 m_aModuleName
= xModuleManager
->identify( m_xFrame
);
143 catch( const css::uno::Exception
& )
147 if ( !m_xConfigManager
.is() )
151 css::uno::Reference
< css::frame::XController
> xController( m_xFrame
->getController() );
152 css::uno::Reference
< css::frame::XModel
> xModel( xController
->getModel() );
153 css::uno::Reference
< css::ui::XUIConfigurationManagerSupplier
> xSupplier( xModel
, css::uno::UNO_QUERY_THROW
);
154 m_xConfigManager
.set( xSupplier
->getUIConfigurationManager() );
155 css::uno::Reference
< css::ui::XUIConfiguration
> xConfig( m_xConfigManager
, css::uno::UNO_QUERY_THROW
);
156 xConfig
->addConfigurationListener( this );
158 catch( const css::uno::RuntimeException
& )
162 if ( !m_xModuleConfigManager
.is() )
166 css::uno::Reference
< css::ui::XModuleUIConfigurationManagerSupplier
> xModuleCfgMgrSupplier(
167 css::ui::theModuleUIConfigurationManagerSupplier::get( m_xContext
) );
168 m_xModuleConfigManager
.set( xModuleCfgMgrSupplier
->getUIConfigurationManager( m_aModuleName
) );
169 css::uno::Reference
< css::ui::XUIConfiguration
> xConfig( m_xModuleConfigManager
, css::uno::UNO_QUERY_THROW
);
170 xConfig
->addConfigurationListener( this );
172 catch ( const css::container::NoSuchElementException
& )
174 SAL_WARN( "fwk.uielement", "Invalid module identifier: " << m_aModuleName
);
176 catch( const css::uno::RuntimeException
& )
180 if ( !m_xMenuContainer
.is() && m_xConfigManager
.is() )
184 m_xMenuContainer
.set( m_xConfigManager
->getSettings( m_aMenuURL
, false ) );
186 catch ( const css::container::NoSuchElementException
& )
188 // Not an error - element may exist only in the module.
190 catch ( const css::lang::IllegalArgumentException
& )
192 SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL
);
197 if ( !m_xMenuContainer
.is() && m_xModuleConfigManager
.is() )
201 m_xMenuContainer
.set( m_xModuleConfigManager
->getSettings( m_aMenuURL
, false ) );
203 catch ( const css::container::NoSuchElementException
& )
205 SAL_WARN( "fwk.uielement", "Can not find settings for " << m_aMenuURL
);
208 catch ( const css::lang::IllegalArgumentException
& )
210 SAL_WARN( "fwk.uielement", "The given URL is not valid: " << m_aMenuURL
);
215 if ( !m_xMenuContainer
.is() )
218 // Clear previous content.
219 if ( m_xMenuBarManager
.is() )
221 m_xMenuBarManager
->dispose();
222 m_xMenuBarManager
.clear();
224 resetPopupMenu( m_xPopupMenu
);
227 // Now fill the menu with the configuration data.
228 framework::MenuBarManager::FillMenu( m_nNewMenuId
, m_xPopupMenu
->GetMenu(), m_aModuleName
, m_xMenuContainer
, m_xDispatchProvider
);
230 // For context menus, add object verbs.
231 if ( !m_bContextMenu
)
234 css::util::URL aObjectMenuURL
;
235 aObjectMenuURL
.Complete
= ".uno:ObjectMenue";
236 m_xURLTransformer
->parseStrict( aObjectMenuURL
);
237 css::uno::Reference
< css::frame::XDispatchProvider
> xDispatchProvider( m_xFrame
, css::uno::UNO_QUERY
);
238 css::uno::Reference
< css::frame::XDispatch
> xDispatch( xDispatchProvider
->queryDispatch( aObjectMenuURL
, OUString(), 0 ) );
239 if ( xDispatch
.is() )
241 xDispatch
->addStatusListener( this, aObjectMenuURL
);
242 xDispatch
->removeStatusListener( this, aObjectMenuURL
);
246 void ResourceMenuController::statusChanged( const css::frame::FeatureStateEvent
& rEvent
)
248 css::uno::Sequence
< css::embed::VerbDescriptor
> aVerbs
;
249 if ( rEvent
.IsEnabled
&& ( rEvent
.State
>>= aVerbs
) )
253 void ResourceMenuController::addVerbs( const css::uno::Sequence
< css::embed::VerbDescriptor
>& rVerbs
)
255 // Check if the document is read-only.
256 css::uno::Reference
< css::frame::XController
> xController( m_xFrame
->getController() );
257 css::uno::Reference
< css::frame::XStorable
> xStorable
;
258 if ( xController
.is() )
259 xStorable
.set( xController
->getModel(), css::uno::UNO_QUERY
);
261 bool bReadOnly
= xStorable
.is() && xStorable
->isReadonly();
262 Menu
* pVCLMenu
= m_xPopupMenu
->GetMenu();
264 for ( const auto& rVerb
: rVerbs
)
266 if ( !( rVerb
.VerbAttributes
& css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU
) ||
267 ( bReadOnly
&& !( rVerb
.VerbAttributes
& css::embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES
) ) )
270 pVCLMenu
->InsertItem( m_nNewMenuId
, rVerb
.VerbName
);
271 pVCLMenu
->SetItemCommand( m_nNewMenuId
, ".uno:ObjectMenue?VerbID:short=" + OUString::number( rVerb
.VerbID
) );
276 void ResourceMenuController::itemActivated( const css::awt::MenuEvent
& /*rEvent*/ )
278 // Must initialize MenuBarManager here, because we want to let the app do context menu interception before.
279 if ( !m_xMenuBarManager
.is() )
281 m_xMenuBarManager
.set( new framework::MenuBarManager(
282 m_xContext
, m_xFrame
, m_xURLTransformer
, m_xDispatchProvider
, m_aModuleName
, m_xPopupMenu
->GetMenu(), false, !m_bContextMenu
&& !m_bInToolbar
) );
283 m_xFrame
->addFrameActionListener( m_xMenuBarManager
);
287 void ResourceMenuController::itemSelected( const css::awt::MenuEvent
& /*rEvent*/ )
289 // Must override this, because we are managed by MenuBarManager, so don't want the handler found in the base class.
292 void ResourceMenuController::elementInserted( const css::ui::ConfigurationEvent
& rEvent
)
294 if ( rEvent
.ResourceURL
== m_aMenuURL
)
295 m_xMenuContainer
.clear();
298 void ResourceMenuController::elementRemoved( const css::ui::ConfigurationEvent
& rEvent
)
300 elementInserted( rEvent
);
303 void ResourceMenuController::elementReplaced( const css::ui::ConfigurationEvent
& rEvent
)
305 elementInserted( rEvent
);
308 void ResourceMenuController::disposing( const css::lang::EventObject
& rEvent
)
310 if ( rEvent
.Source
== m_xConfigManager
)
311 m_xConfigManager
.clear();
312 else if ( rEvent
.Source
== m_xModuleConfigManager
)
313 m_xModuleConfigManager
.clear();
316 if ( m_xMenuBarManager
.is() )
319 m_xFrame
->removeFrameActionListener( m_xMenuBarManager
);
321 m_xMenuBarManager
->dispose();
322 m_xMenuBarManager
.clear();
324 svt::PopupMenuControllerBase::disposing( rEvent
);
328 void ResourceMenuController::disposing(std::unique_lock
<std::mutex
>& rGuard
)
330 css::uno::Reference
< css::ui::XUIConfiguration
> xConfig( m_xConfigManager
, css::uno::UNO_QUERY
);
332 xConfig
->removeConfigurationListener( this );
334 css::uno::Reference
< css::ui::XUIConfiguration
> xModuleConfig( m_xModuleConfigManager
, css::uno::UNO_QUERY
);
335 if ( xModuleConfig
.is() )
336 xModuleConfig
->removeConfigurationListener( this );
338 m_xConfigManager
.clear();
339 m_xModuleConfigManager
.clear();
340 m_xMenuContainer
.clear();
341 m_xDispatchProvider
.clear();
342 if ( m_xMenuBarManager
.is() )
345 m_xFrame
->removeFrameActionListener( m_xMenuBarManager
);
347 m_xMenuBarManager
->dispose();
348 m_xMenuBarManager
.clear();
351 svt::PopupMenuControllerBase::disposing(rGuard
);
354 OUString
ResourceMenuController::getImplementationName()
356 if ( m_bToolbarContainer
)
357 return "com.sun.star.comp.framework.ToolbarAsMenuController";
359 return "com.sun.star.comp.framework.ResourceMenuController";
362 css::uno::Sequence
< OUString
> ResourceMenuController::getSupportedServiceNames()
364 return { "com.sun.star.frame.PopupMenuController" };
367 class SaveAsMenuController
: public ResourceMenuController
370 SaveAsMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rContext
,
371 const css::uno::Sequence
< css::uno::Any
>& rArgs
);
374 virtual OUString SAL_CALL
getImplementationName() override
;
377 virtual void impl_setPopupMenu() override
;
380 SaveAsMenuController::SaveAsMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rContext
,
381 const css::uno::Sequence
< css::uno::Any
>& rArgs
)
382 : ResourceMenuController( rContext
, rArgs
, false )
386 void InsertItem(const css::uno::Reference
<css::awt::XPopupMenu
>& rPopupMenu
,
387 const OUString
& rCommand
)
389 sal_uInt16 nItemId
= rPopupMenu
->getItemCount() + 1;
390 rPopupMenu
->insertItem(nItemId
, OUString(), 0, -1);
391 rPopupMenu
->setCommand(nItemId
, rCommand
);
394 void SaveAsMenuController::impl_setPopupMenu()
396 SolarMutexGuard aGuard
;
398 InsertItem(m_xPopupMenu
, ".uno:SaveAs");
399 InsertItem(m_xPopupMenu
, ".uno:ExportTo");
400 InsertItem(m_xPopupMenu
, ".uno:SaveACopy");
401 InsertItem(m_xPopupMenu
, ".uno:SaveAsTemplate");
402 m_xPopupMenu
->insertSeparator(-1);
403 InsertItem(m_xPopupMenu
, ".uno:SaveAsRemote");
406 OUString
SaveAsMenuController::getImplementationName()
408 return "com.sun.star.comp.framework.SaveAsMenuController";
411 class WindowListMenuController
: public ResourceMenuController
414 WindowListMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rxContext
,
415 const css::uno::Sequence
< css::uno::Any
>& rxArgs
)
416 : ResourceMenuController(rxContext
, rxArgs
, false) {}
419 void SAL_CALL
itemActivated( const css::awt::MenuEvent
& rEvent
) override
;
420 void SAL_CALL
itemSelected( const css::awt::MenuEvent
& rEvent
) override
;
423 OUString SAL_CALL
getImplementationName() override
;
426 void impl_setPopupMenu() override
;
429 constexpr sal_uInt16 START_ITEMID_WINDOWLIST
= 4600;
430 constexpr sal_uInt16 END_ITEMID_WINDOWLIST
= 4699;
432 void WindowListMenuController::itemActivated( const css::awt::MenuEvent
& rEvent
)
434 ResourceMenuController::itemActivated( rEvent
);
436 // update window list
437 ::std::vector
< OUString
> aNewWindowListVector
;
439 css::uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create( m_xContext
);
441 sal_uInt16 nActiveItemId
= 0;
442 sal_uInt16 nItemId
= START_ITEMID_WINDOWLIST
;
444 css::uno::Reference
< css::frame::XFrame
> xCurrentFrame
= xDesktop
->getCurrentFrame();
445 css::uno::Reference
< css::container::XIndexAccess
> xList
= xDesktop
->getFrames();
446 sal_Int32 nFrameCount
= xList
->getCount();
447 aNewWindowListVector
.reserve(nFrameCount
);
448 for (sal_Int32 i
=0; i
<nFrameCount
; ++i
)
450 css::uno::Reference
< css::frame::XFrame
> xFrame
;
451 xList
->getByIndex(i
) >>= xFrame
;
455 if ( xFrame
== xCurrentFrame
)
456 nActiveItemId
= nItemId
;
458 VclPtr
<vcl::Window
> pWin
= VCLUnoHelper::GetWindow( xFrame
->getContainerWindow() );
459 OUString sWindowTitle
;
460 if ( pWin
&& pWin
->IsVisible() )
461 sWindowTitle
= pWin
->GetText();
463 // tdf#101658 In case the frame is embedded somewhere, LO has no control over it.
464 // So we just skip it.
465 if ( sWindowTitle
.isEmpty() )
468 aNewWindowListVector
.push_back( sWindowTitle
);
476 Menu
* pVCLMenu
= m_xPopupMenu
->GetMenu();
477 int nItemCount
= pVCLMenu
->GetItemCount();
479 if ( nItemCount
> 0 )
481 // remove all old window list entries from menu
482 sal_uInt16 nPos
= pVCLMenu
->GetItemPos( START_ITEMID_WINDOWLIST
);
483 for ( sal_uInt16 n
= nPos
; n
< pVCLMenu
->GetItemCount(); )
484 pVCLMenu
->RemoveItem( n
);
486 if ( pVCLMenu
->GetItemType( pVCLMenu
->GetItemCount()-1 ) == MenuItemType::SEPARATOR
)
487 pVCLMenu
->RemoveItem( pVCLMenu
->GetItemCount()-1 );
490 if ( !aNewWindowListVector
.empty() )
492 // append new window list entries to menu
493 pVCLMenu
->InsertSeparator();
494 nItemId
= START_ITEMID_WINDOWLIST
;
495 const sal_uInt32 nCount
= aNewWindowListVector
.size();
496 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
498 pVCLMenu
->InsertItem( nItemId
, aNewWindowListVector
.at( i
), MenuItemBits::RADIOCHECK
);
499 if ( nItemId
== nActiveItemId
)
500 pVCLMenu
->CheckItem( nItemId
);
507 void WindowListMenuController::itemSelected( const css::awt::MenuEvent
& rEvent
)
509 if ( rEvent
.MenuId
< START_ITEMID_WINDOWLIST
|| rEvent
.MenuId
> END_ITEMID_WINDOWLIST
)
512 // window list menu item selected
513 css::uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create( m_xContext
);
515 sal_uInt16 nTaskId
= START_ITEMID_WINDOWLIST
;
516 css::uno::Reference
< css::container::XIndexAccess
> xList
= xDesktop
->getFrames();
517 sal_Int32 nCount
= xList
->getCount();
518 for ( sal_Int32 i
=0; i
<nCount
; ++i
)
520 css::uno::Reference
< css::frame::XFrame
> xFrame
;
521 xList
->getByIndex(i
) >>= xFrame
;
522 if ( xFrame
.is() && nTaskId
== rEvent
.MenuId
)
524 VclPtr
<vcl::Window
> pWin
= VCLUnoHelper::GetWindow( xFrame
->getContainerWindow() );
526 pWin
->ToTop( ToTopFlags::RestoreWhenMin
);
534 void WindowListMenuController::impl_setPopupMenu()
536 // Make this controller work also with initially empty
537 // menu, which PopupMenu::ImplExecute doesn't allow.
538 if (m_xPopupMenu
.is() && !m_xPopupMenu
->getItemCount())
539 m_xPopupMenu
->insertSeparator(0);
542 OUString
WindowListMenuController::getImplementationName()
544 return "com.sun.star.comp.framework.WindowListMenuController";
549 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
550 com_sun_star_comp_framework_ResourceMenuController_get_implementation(
551 css::uno::XComponentContext
* context
,
552 css::uno::Sequence
< css::uno::Any
> const & args
)
554 return cppu::acquire( new ResourceMenuController( context
, args
, false ) );
557 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
558 com_sun_star_comp_framework_ToolbarAsMenuController_get_implementation(
559 css::uno::XComponentContext
* context
,
560 css::uno::Sequence
< css::uno::Any
> const & args
)
562 return cppu::acquire( new ResourceMenuController( context
, args
, true ) );
565 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
566 com_sun_star_comp_framework_WindowListMenuController_get_implementation(
567 css::uno::XComponentContext
* context
,
568 css::uno::Sequence
< css::uno::Any
> const & args
)
570 return cppu::acquire( new WindowListMenuController( context
, args
) );
573 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
574 com_sun_star_comp_framework_SaveAsMenuController_get_implementation(
575 css::uno::XComponentContext
* context
,
576 css::uno::Sequence
< css::uno::Any
> const & args
)
578 return cppu::acquire( new SaveAsMenuController( context
, args
) );
581 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */