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 SAL_CALL
disposing() 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
, comphelper::getFromUnoTunnel
<VCLXMenu
>( 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 VCLXMenu
* pAwtMenu
= comphelper::getFromUnoTunnel
<VCLXMenu
>( m_xPopupMenu
);
263 Menu
* pVCLMenu
= pAwtMenu
->GetMenu();
265 for ( const auto& rVerb
: rVerbs
)
267 if ( !( rVerb
.VerbAttributes
& css::embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU
) ||
268 ( bReadOnly
&& !( rVerb
.VerbAttributes
& css::embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES
) ) )
271 pVCLMenu
->InsertItem( m_nNewMenuId
, rVerb
.VerbName
);
272 pVCLMenu
->SetItemCommand( m_nNewMenuId
, ".uno:ObjectMenue?VerbID:short=" + OUString::number( rVerb
.VerbID
) );
277 void ResourceMenuController::itemActivated( const css::awt::MenuEvent
& /*rEvent*/ )
279 // Must initialize MenuBarManager here, because we want to let the app do context menu interception before.
280 if ( !m_xMenuBarManager
.is() )
282 VCLXMenu
* pAwtMenu
= comphelper::getFromUnoTunnel
<VCLXMenu
>( m_xPopupMenu
);
283 m_xMenuBarManager
.set( new framework::MenuBarManager(
284 m_xContext
, m_xFrame
, m_xURLTransformer
, m_xDispatchProvider
, m_aModuleName
, pAwtMenu
->GetMenu(), false, !m_bContextMenu
&& !m_bInToolbar
) );
285 m_xFrame
->addFrameActionListener( m_xMenuBarManager
);
289 void ResourceMenuController::itemSelected( const css::awt::MenuEvent
& /*rEvent*/ )
291 // Must override this, because we are managed by MenuBarManager, so don't want the handler found in the base class.
294 void ResourceMenuController::elementInserted( const css::ui::ConfigurationEvent
& rEvent
)
296 if ( rEvent
.ResourceURL
== m_aMenuURL
)
297 m_xMenuContainer
.clear();
300 void ResourceMenuController::elementRemoved( const css::ui::ConfigurationEvent
& rEvent
)
302 elementInserted( rEvent
);
305 void ResourceMenuController::elementReplaced( const css::ui::ConfigurationEvent
& rEvent
)
307 elementInserted( rEvent
);
310 void ResourceMenuController::disposing( const css::lang::EventObject
& rEvent
)
312 if ( rEvent
.Source
== m_xConfigManager
)
313 m_xConfigManager
.clear();
314 else if ( rEvent
.Source
== m_xModuleConfigManager
)
315 m_xModuleConfigManager
.clear();
318 if ( m_xMenuBarManager
.is() )
321 m_xFrame
->removeFrameActionListener( m_xMenuBarManager
);
323 m_xMenuBarManager
->dispose();
324 m_xMenuBarManager
.clear();
326 svt::PopupMenuControllerBase::disposing( rEvent
);
330 void ResourceMenuController::disposing()
332 css::uno::Reference
< css::ui::XUIConfiguration
> xConfig( m_xConfigManager
, css::uno::UNO_QUERY
);
334 xConfig
->removeConfigurationListener( this );
336 css::uno::Reference
< css::ui::XUIConfiguration
> xModuleConfig( m_xModuleConfigManager
, css::uno::UNO_QUERY
);
337 if ( xModuleConfig
.is() )
338 xModuleConfig
->removeConfigurationListener( this );
340 m_xConfigManager
.clear();
341 m_xModuleConfigManager
.clear();
342 m_xMenuContainer
.clear();
343 m_xDispatchProvider
.clear();
344 if ( m_xMenuBarManager
.is() )
347 m_xFrame
->removeFrameActionListener( m_xMenuBarManager
);
349 m_xMenuBarManager
->dispose();
350 m_xMenuBarManager
.clear();
353 svt::PopupMenuControllerBase::disposing();
356 OUString
ResourceMenuController::getImplementationName()
358 if ( m_bToolbarContainer
)
359 return "com.sun.star.comp.framework.ToolbarAsMenuController";
361 return "com.sun.star.comp.framework.ResourceMenuController";
364 css::uno::Sequence
< OUString
> ResourceMenuController::getSupportedServiceNames()
366 return { "com.sun.star.frame.PopupMenuController" };
369 class SaveAsMenuController
: public ResourceMenuController
372 SaveAsMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rContext
,
373 const css::uno::Sequence
< css::uno::Any
>& rArgs
);
376 virtual OUString SAL_CALL
getImplementationName() override
;
379 virtual void impl_setPopupMenu() override
;
382 SaveAsMenuController::SaveAsMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rContext
,
383 const css::uno::Sequence
< css::uno::Any
>& rArgs
)
384 : ResourceMenuController( rContext
, rArgs
, false )
388 void InsertItem(const css::uno::Reference
<css::awt::XPopupMenu
>& rPopupMenu
,
389 const OUString
& rCommand
)
391 sal_uInt16 nItemId
= rPopupMenu
->getItemCount() + 1;
392 rPopupMenu
->insertItem(nItemId
, OUString(), 0, -1);
393 rPopupMenu
->setCommand(nItemId
, rCommand
);
396 void SaveAsMenuController::impl_setPopupMenu()
398 SolarMutexGuard aGuard
;
400 InsertItem(m_xPopupMenu
, ".uno:SaveAs");
401 InsertItem(m_xPopupMenu
, ".uno:ExportTo");
402 InsertItem(m_xPopupMenu
, ".uno:SaveACopy");
403 InsertItem(m_xPopupMenu
, ".uno:SaveAsTemplate");
404 m_xPopupMenu
->insertSeparator(-1);
405 InsertItem(m_xPopupMenu
, ".uno:SaveAsRemote");
408 OUString
SaveAsMenuController::getImplementationName()
410 return "com.sun.star.comp.framework.SaveAsMenuController";
413 class WindowListMenuController
: public ResourceMenuController
416 WindowListMenuController( const css::uno::Reference
< css::uno::XComponentContext
>& rxContext
,
417 const css::uno::Sequence
< css::uno::Any
>& rxArgs
)
418 : ResourceMenuController(rxContext
, rxArgs
, false) {}
421 void SAL_CALL
itemActivated( const css::awt::MenuEvent
& rEvent
) override
;
422 void SAL_CALL
itemSelected( const css::awt::MenuEvent
& rEvent
) override
;
425 OUString SAL_CALL
getImplementationName() override
;
428 void impl_setPopupMenu() override
;
431 constexpr sal_uInt16 START_ITEMID_WINDOWLIST
= 4600;
432 constexpr sal_uInt16 END_ITEMID_WINDOWLIST
= 4699;
434 void WindowListMenuController::itemActivated( const css::awt::MenuEvent
& rEvent
)
436 ResourceMenuController::itemActivated( rEvent
);
438 // update window list
439 ::std::vector
< OUString
> aNewWindowListVector
;
441 css::uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create( m_xContext
);
443 sal_uInt16 nActiveItemId
= 0;
444 sal_uInt16 nItemId
= START_ITEMID_WINDOWLIST
;
446 css::uno::Reference
< css::frame::XFrame
> xCurrentFrame
= xDesktop
->getCurrentFrame();
447 css::uno::Reference
< css::container::XIndexAccess
> xList
= xDesktop
->getFrames();
448 sal_Int32 nFrameCount
= xList
->getCount();
449 aNewWindowListVector
.reserve(nFrameCount
);
450 for (sal_Int32 i
=0; i
<nFrameCount
; ++i
)
452 css::uno::Reference
< css::frame::XFrame
> xFrame
;
453 xList
->getByIndex(i
) >>= xFrame
;
457 if ( xFrame
== xCurrentFrame
)
458 nActiveItemId
= nItemId
;
460 VclPtr
<vcl::Window
> pWin
= VCLUnoHelper::GetWindow( xFrame
->getContainerWindow() );
461 OUString sWindowTitle
;
462 if ( pWin
&& pWin
->IsVisible() )
463 sWindowTitle
= pWin
->GetText();
465 // tdf#101658 In case the frame is embedded somewhere, LO has no control over it.
466 // So we just skip it.
467 if ( sWindowTitle
.isEmpty() )
470 aNewWindowListVector
.push_back( sWindowTitle
);
478 VCLXMenu
* pAwtMenu
= comphelper::getFromUnoTunnel
<VCLXMenu
>( m_xPopupMenu
);
479 Menu
* pVCLMenu
= pAwtMenu
->GetMenu();
480 int nItemCount
= pVCLMenu
->GetItemCount();
482 if ( nItemCount
> 0 )
484 // remove all old window list entries from menu
485 sal_uInt16 nPos
= pVCLMenu
->GetItemPos( START_ITEMID_WINDOWLIST
);
486 for ( sal_uInt16 n
= nPos
; n
< pVCLMenu
->GetItemCount(); )
487 pVCLMenu
->RemoveItem( n
);
489 if ( pVCLMenu
->GetItemType( pVCLMenu
->GetItemCount()-1 ) == MenuItemType::SEPARATOR
)
490 pVCLMenu
->RemoveItem( pVCLMenu
->GetItemCount()-1 );
493 if ( !aNewWindowListVector
.empty() )
495 // append new window list entries to menu
496 pVCLMenu
->InsertSeparator();
497 nItemId
= START_ITEMID_WINDOWLIST
;
498 const sal_uInt32 nCount
= aNewWindowListVector
.size();
499 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
501 pVCLMenu
->InsertItem( nItemId
, aNewWindowListVector
.at( i
), MenuItemBits::RADIOCHECK
);
502 if ( nItemId
== nActiveItemId
)
503 pVCLMenu
->CheckItem( nItemId
);
510 void WindowListMenuController::itemSelected( const css::awt::MenuEvent
& rEvent
)
512 if ( rEvent
.MenuId
< START_ITEMID_WINDOWLIST
|| rEvent
.MenuId
> END_ITEMID_WINDOWLIST
)
515 // window list menu item selected
516 css::uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create( m_xContext
);
518 sal_uInt16 nTaskId
= START_ITEMID_WINDOWLIST
;
519 css::uno::Reference
< css::container::XIndexAccess
> xList
= xDesktop
->getFrames();
520 sal_Int32 nCount
= xList
->getCount();
521 for ( sal_Int32 i
=0; i
<nCount
; ++i
)
523 css::uno::Reference
< css::frame::XFrame
> xFrame
;
524 xList
->getByIndex(i
) >>= xFrame
;
525 if ( xFrame
.is() && nTaskId
== rEvent
.MenuId
)
527 VclPtr
<vcl::Window
> pWin
= VCLUnoHelper::GetWindow( xFrame
->getContainerWindow() );
529 pWin
->ToTop( ToTopFlags::RestoreWhenMin
);
537 void WindowListMenuController::impl_setPopupMenu()
539 // Make this controller work also with initially empty
540 // menu, which PopupMenu::ImplExecute doesn't allow.
541 if (m_xPopupMenu
.is() && !m_xPopupMenu
->getItemCount())
542 m_xPopupMenu
->insertSeparator(0);
545 OUString
WindowListMenuController::getImplementationName()
547 return "com.sun.star.comp.framework.WindowListMenuController";
552 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
553 com_sun_star_comp_framework_ResourceMenuController_get_implementation(
554 css::uno::XComponentContext
* context
,
555 css::uno::Sequence
< css::uno::Any
> const & args
)
557 return cppu::acquire( new ResourceMenuController( context
, args
, false ) );
560 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
561 com_sun_star_comp_framework_ToolbarAsMenuController_get_implementation(
562 css::uno::XComponentContext
* context
,
563 css::uno::Sequence
< css::uno::Any
> const & args
)
565 return cppu::acquire( new ResourceMenuController( context
, args
, true ) );
568 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
569 com_sun_star_comp_framework_WindowListMenuController_get_implementation(
570 css::uno::XComponentContext
* context
,
571 css::uno::Sequence
< css::uno::Any
> const & args
)
573 return cppu::acquire( new WindowListMenuController( context
, args
) );
576 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
577 com_sun_star_comp_framework_SaveAsMenuController_get_implementation(
578 css::uno::XComponentContext
* context
,
579 css::uno::Sequence
< css::uno::Any
> const & args
)
581 return cppu::acquire( new SaveAsMenuController( context
, args
) );
584 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */