1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <uielement/menubarmanager.hxx>
21 #include <uielement/styletoolbarcontroller.hxx>
22 #include <framework/menuconfiguration.hxx>
23 #include <framework/addonmenu.hxx>
24 #include <framework/addonsoptions.hxx>
25 #include <classes/fwkresid.hxx>
26 #include <helper/mischelper.hxx>
27 #include <strings.hrc>
28 #include <services.h>
29 
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <com/sun/star/frame/XDispatch.hpp>
32 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/lang/DisposedException.hpp>
35 #include <com/sun/star/frame/Desktop.hpp>
36 #include <com/sun/star/container/XEnumeration.hpp>
37 #include <com/sun/star/util/XStringWidth.hpp>
38 #include <com/sun/star/uno/XComponentContext.hpp>
39 #include <com/sun/star/uno/XCurrentContext.hpp>
40 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
41 #include <com/sun/star/frame/XPopupMenuController.hpp>
42 #include <com/sun/star/frame/thePopupMenuControllerFactory.hpp>
43 #include <com/sun/star/lang/SystemDependent.hpp>
44 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
45 #include <com/sun/star/ui/ItemType.hpp>
46 #include <com/sun/star/ui/ImageType.hpp>
47 #include <com/sun/star/container/XNameAccess.hpp>
48 #include <com/sun/star/frame/ModuleManager.hpp>
49 #include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
50 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
51 #include <com/sun/star/ui/ItemStyle.hpp>
52 #include <com/sun/star/frame/status/Visibility.hpp>
53 #include <com/sun/star/util/URLTransformer.hpp>
54 
55 #include <comphelper/processfactory.hxx>
56 #include <comphelper/propertyvalue.hxx>
57 #include <svtools/menuoptions.hxx>
58 #include <svtools/javainteractionhandler.hxx>
59 #include <uno/current_context.hxx>
60 #include <unotools/historyoptions.hxx>
61 #include <unotools/pathoptions.hxx>
62 #include <unotools/cmdoptions.hxx>
63 #include <unotools/localfilehelper.hxx>
64 #include <toolkit/awt/vclxmenu.hxx>
65 #include <toolkit/helper/vclunohelper.hxx>
66 #include <vcl/svapp.hxx>
67 #include <vcl/sysdata.hxx>
68 #include <vcl/window.hxx>
69 #include <vcl/menu.hxx>
70 #include <vcl/settings.hxx>
71 #include <vcl/commandinfoprovider.hxx>
72 #include <osl/file.hxx>
73 #include <sal/log.hxx>
74 #include <svtools/acceleratorexecute.hxx>
75 #include <svtools/miscopt.hxx>
76 #include <uielement/menubarmerger.hxx>
77 #include <tools/urlobj.hxx>
78 
79 using namespace ::cppu;
80 using namespace ::com::sun::star;
81 using namespace ::com::sun::star::uno;
82 using namespace ::com::sun::star::util;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::frame;
85 using namespace ::com::sun::star::container;
86 using namespace ::com::sun::star::lang;
87 using namespace ::com::sun::star::ui;
88 
89 const sal_uInt16 ADDONMENU_MERGE_ITEMID_START = 1500;
90 
91 namespace framework
92 {
93 
94 #define aCmdHelpIndex ".uno:HelpIndex"
95 #define aCmdToolsMenu ".uno:ToolsMenu"
96 #define aCmdHelpMenu ".uno:HelpMenu"
97 #define aSpecialWindowCommand ".uno:WindowList"
98 
MenuBarManager(const Reference<XComponentContext> & rxContext,const Reference<XFrame> & rFrame,const Reference<XURLTransformer> & _xURLTransformer,const Reference<XDispatchProvider> & rDispatchProvider,const OUString & rModuleIdentifier,Menu * pMenu,bool bDelete,bool bHasMenuBar)99 MenuBarManager::MenuBarManager(
100     const Reference< XComponentContext >& rxContext,
101     const Reference< XFrame >& rFrame,
102     const Reference< XURLTransformer >& _xURLTransformer,
103     const Reference< XDispatchProvider >& rDispatchProvider,
104     const OUString& rModuleIdentifier,
105     Menu* pMenu, bool bDelete, bool bHasMenuBar ):
106     WeakComponentImplHelper( m_aMutex )
107     , m_bRetrieveImages( false )
108     , m_bAcceleratorCfg( false )
109     , m_bModuleIdentified( false )
110     , m_bHasMenuBar( bHasMenuBar )
111     , m_xContext(rxContext)
112     , m_xURLTransformer(_xURLTransformer)
113     , m_sIconTheme( SvtMiscOptions().GetIconTheme() )
114 {
115     m_aAsyncSettingsTimer.SetDebugName( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" );
116     m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(m_xContext);
117     FillMenuManager( pMenu, rFrame, rDispatchProvider, rModuleIdentifier, bDelete );
118 }
119 
MenuBarManager(const Reference<XComponentContext> & rxContext,const Reference<XFrame> & rFrame,const Reference<XURLTransformer> & _xURLTransformer,Menu * pAddonMenu,bool popup)120 MenuBarManager::MenuBarManager(
121     const Reference< XComponentContext >& rxContext,
122     const Reference< XFrame >& rFrame,
123     const Reference< XURLTransformer >& _xURLTransformer,
124     Menu* pAddonMenu,
125     bool popup):
126     WeakComponentImplHelper( m_aMutex )
127     , m_bRetrieveImages( true )
128     , m_bAcceleratorCfg( false )
129     , m_bModuleIdentified( false )
130     , m_bHasMenuBar( true )
131     , m_xContext(rxContext)
132     , m_xURLTransformer(_xURLTransformer)
133     , m_sIconTheme( SvtMiscOptions().GetIconTheme() )
134 {
135     m_aAsyncSettingsTimer.SetDebugName( "framework::MenuBarManager::Deactivate m_aAsyncSettingsTimer" );
136     Init(rFrame,pAddonMenu, popup);
137 }
138 
getMenuHandle(const Sequence<sal_Int8> &,sal_Int16 SystemType)139 Any SAL_CALL MenuBarManager::getMenuHandle( const Sequence< sal_Int8 >& /*ProcessId*/, sal_Int16 SystemType )
140 {
141     SolarMutexGuard aSolarGuard;
142 
143     if ( rBHelper.bDisposed || rBHelper.bInDispose )
144         throw css::lang::DisposedException();
145 
146     Any a;
147 
148     if ( m_pVCLMenu )
149     {
150         SystemMenuData aSystemMenuData;
151 
152         m_pVCLMenu->GetSystemMenuData( &aSystemMenuData );
153 #ifdef _WIN32
154         if( SystemType == SystemDependent::SYSTEM_WIN32 )
155         {
156             a <<= sal_Int64(
157                 reinterpret_cast<sal_IntPtr>(aSystemMenuData.hMenu));
158         }
159 #else
160         (void) SystemType;
161 #endif
162     }
163 
164     return a;
165 }
166 
~MenuBarManager()167 MenuBarManager::~MenuBarManager()
168 {
169     // stop asynchronous settings timer
170     m_xDeferedItemContainer.clear();
171     m_aAsyncSettingsTimer.Stop();
172 
173     SAL_WARN_IF( OWeakObject::m_refCount != 0, "fwk.uielement", "Who wants to delete an object with refcount > 0!" );
174 }
175 
Destroy()176 void MenuBarManager::Destroy()
177 {
178     SolarMutexGuard aGuard;
179 
180     if ( !rBHelper.bDisposed )
181     {
182         // stop asynchronous settings timer and
183         // release deferred item container reference
184         m_aAsyncSettingsTimer.Stop();
185         m_xDeferedItemContainer.clear();
186         RemoveListener();
187 
188         m_aMenuItemHandlerVector.clear();
189 
190         if ( m_bDeleteMenu )
191         {
192             m_pVCLMenu.disposeAndClear();
193         }
194     }
195 }
196 
197 // XComponent
disposing()198 void SAL_CALL MenuBarManager::disposing()
199 {
200     Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );
201 
202     SolarMutexGuard g;
203     Destroy();
204 
205     if ( m_xDocImageManager.is() )
206     {
207         try
208         {
209             m_xDocImageManager->removeConfigurationListener(
210                 Reference< XUIConfigurationListener >(
211                     static_cast< OWeakObject* >( this ), UNO_QUERY ));
212         }
213         catch ( const Exception& )
214         {
215         }
216     }
217     if ( m_xModuleImageManager.is() )
218     {
219         try
220         {
221             m_xModuleImageManager->removeConfigurationListener(
222                 Reference< XUIConfigurationListener >(
223                     static_cast< OWeakObject* >( this ), UNO_QUERY ));
224         }
225         catch ( const Exception& )
226         {
227         }
228     }
229     m_xDocImageManager.clear();
230     m_xModuleImageManager.clear();
231     m_xGlobalAcceleratorManager.clear();
232     m_xModuleAcceleratorManager.clear();
233     m_xDocAcceleratorManager.clear();
234     m_xPopupMenuControllerFactory.clear();
235     m_xContext.clear();
236 }
237 
elementInserted(const css::ui::ConfigurationEvent & Event)238 void SAL_CALL MenuBarManager::elementInserted( const css::ui::ConfigurationEvent& Event )
239 {
240     SolarMutexGuard g;
241 
242     /* SAFE AREA ----------------------------------------------------------------------------------------------- */
243     if ( rBHelper.bDisposed || rBHelper.bInDispose )
244         return;
245 
246     sal_Int16 nImageType = sal_Int16();
247     if (( Event.aInfo >>= nImageType ) &&
248         ( nImageType == css::ui::ImageType::SIZE_LARGE ))
249         RequestImages();
250 }
251 
elementRemoved(const css::ui::ConfigurationEvent & Event)252 void SAL_CALL MenuBarManager::elementRemoved( const css::ui::ConfigurationEvent& Event )
253 {
254     elementInserted(Event);
255 }
256 
elementReplaced(const css::ui::ConfigurationEvent & Event)257 void SAL_CALL MenuBarManager::elementReplaced( const css::ui::ConfigurationEvent& Event )
258 {
259     elementInserted(Event);
260 }
261 
262 // XFrameActionListener
frameAction(const FrameActionEvent & Action)263 void SAL_CALL MenuBarManager::frameAction( const FrameActionEvent& Action )
264 {
265     SolarMutexGuard g;
266 
267     if ( rBHelper.bDisposed || rBHelper.bInDispose )
268         throw css::lang::DisposedException();
269 
270     if ( Action.Action == FrameAction_CONTEXT_CHANGED )
271     {
272         for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
273         {
274             // Clear dispatch reference as we will requery it later
275             if ( menuItemHandler->xMenuItemDispatch.is() )
276             {
277                 URL aTargetURL;
278                 aTargetURL.Complete = menuItemHandler->aMenuItemURL;
279                 m_xURLTransformer->parseStrict( aTargetURL );
280 
281                 menuItemHandler->xMenuItemDispatch->removeStatusListener( this, aTargetURL );
282             }
283             menuItemHandler->xMenuItemDispatch.clear();
284         }
285     }
286 }
287 
288 // XStatusListener
statusChanged(const FeatureStateEvent & Event)289 void SAL_CALL MenuBarManager::statusChanged( const FeatureStateEvent& Event )
290 {
291     OUString aFeatureURL = Event.FeatureURL.Complete;
292 
293     SolarMutexGuard aSolarGuard;
294     {
295         if ( rBHelper.bDisposed || rBHelper.bInDispose )
296             return;
297 
298         // We have to check all menu entries as there can be identical entries in a popup menu.
299         for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
300         {
301             if ( menuItemHandler->aParsedItemURL == aFeatureURL )
302             {
303                 bool            bCheckmark( false );
304                 bool            bMenuItemEnabled( m_pVCLMenu->IsItemEnabled( menuItemHandler->nItemId ));
305                 bool            bEnabledItem( Event.IsEnabled );
306                 OUString       aItemText;
307                 status::Visibility  aVisibilityStatus;
308 
309                 #ifdef UNIX
310                 //enable some slots hardly, because UNIX clipboard does not notify all changes
311                 // Can be removed if follow up task will be fixed directly within applications.
312                 // Note: PasteSpecial is handled specifically by calc
313                 // Calc also disables Paste under some circumstances, do not override.
314                 /* TODO: is this workaround even needed anymore? Was introduced
315                  * in 2009 with commit 426ab2c0e8f6e3fe2b766f74f6b8da873d860260
316                  * as some "metropatch" and the other places it touched seem to
317                  * be gone. */
318                 if ( (menuItemHandler->aMenuItemURL == ".uno:Paste" &&
319                             m_aModuleIdentifier != "com.sun.star.sheet.SpreadsheetDocument")
320                         || menuItemHandler->aMenuItemURL == ".uno:PasteClipboard" )    // special for draw/impress
321                     bEnabledItem = true;
322                 #endif
323 
324                 // Enable/disable item
325                 if ( bEnabledItem != bMenuItemEnabled )
326                 {
327                     m_pVCLMenu->EnableItem( menuItemHandler->nItemId, bEnabledItem );
328 
329                     // Remove "checked" mark for disabled menu items.
330                     // Initially disabled but checkable menu items do not receive
331                     // checked/unchecked state, so can appear inconsistently after
332                     // enabling/disabling. Since we can not pass checked state for disabled
333                     // items, we will just reset checked state for them, anyway correct state
334                     // will be transferred from controller once item enabled.
335                     if ( !bEnabledItem && m_pVCLMenu->IsItemChecked( menuItemHandler->nItemId ) )
336                         m_pVCLMenu->CheckItem( menuItemHandler->nItemId, false );
337                 }
338 
339                 if ( Event.State >>= bCheckmark )
340                 {
341                     // Checkmark or RadioButton
342                     m_pVCLMenu->CheckItem( menuItemHandler->nItemId, bCheckmark );
343                     // If not already designated RadioButton set as CheckMark
344                     MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId );
345                     if (!(nBits & MenuItemBits::RADIOCHECK))
346                         m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE );
347 
348                     if ( menuItemHandler->bMadeInvisible )
349                         m_pVCLMenu->ShowItem( menuItemHandler->nItemId );
350                 }
351                 else if ( Event.State >>= aItemText )
352                 {
353                     INetURLObject aURL( aFeatureURL );
354                     OUString aEnumPart = aURL.GetURLPath().getToken( 1, '.' );
355                     if ( !aEnumPart.isEmpty() && aURL.GetProtocol() == INetProtocol::Uno )
356                     {
357                         // Checkmark or RadioButton
358                         m_pVCLMenu->CheckItem( menuItemHandler->nItemId, aItemText == aEnumPart );
359                         // If not already designated RadioButton set as CheckMark
360                         MenuItemBits nBits = m_pVCLMenu->GetItemBits( menuItemHandler->nItemId );
361                         if (!(nBits & MenuItemBits::RADIOCHECK))
362                             m_pVCLMenu->SetItemBits( menuItemHandler->nItemId, nBits | MenuItemBits::CHECKABLE );
363                     }
364                     else
365                     {
366                         // Replacement for place holders
367                         if ( aItemText.startsWith("($1)") )
368                         {
369                             aItemText = FwkResId(STR_UPDATEDOC) + " " + aItemText.copy( 4 );
370                         }
371                         else if ( aItemText.startsWith("($2)") )
372                         {
373                             aItemText = FwkResId(STR_CLOSEDOC_ANDRETURN) + aItemText.copy( 4 );
374                         }
375                         else if ( aItemText.startsWith("($3)") )
376                         {
377                             aItemText = FwkResId(STR_SAVECOPYDOC) + aItemText.copy( 4 );
378                         }
379 
380                         m_pVCLMenu->SetItemText( menuItemHandler->nItemId, aItemText );
381                     }
382 
383                     if ( menuItemHandler->bMadeInvisible )
384                         m_pVCLMenu->ShowItem( menuItemHandler->nItemId );
385                 }
386                 else if ( Event.State >>= aVisibilityStatus )
387                 {
388                     // Visibility
389                     m_pVCLMenu->ShowItem( menuItemHandler->nItemId, aVisibilityStatus.bVisible );
390                     menuItemHandler->bMadeInvisible = !aVisibilityStatus.bVisible;
391                 }
392                 else if ( menuItemHandler->bMadeInvisible )
393                     m_pVCLMenu->ShowItem( menuItemHandler->nItemId );
394             }
395 
396             if ( Event.Requery )
397             {
398                 // Release dispatch object - will be required on the next activate!
399                 menuItemHandler->xMenuItemDispatch.clear();
400             }
401         }
402     }
403 }
404 
405 // Helper to retrieve own structure from item ID
GetMenuItemHandler(sal_uInt16 nItemId)406 MenuBarManager::MenuItemHandler* MenuBarManager::GetMenuItemHandler( sal_uInt16 nItemId )
407 {
408     SolarMutexGuard g;
409 
410     for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
411     {
412         if ( menuItemHandler->nItemId == nItemId )
413             return menuItemHandler.get();
414     }
415 
416     return nullptr;
417 }
418 
419 // Helper to set request images flag
RequestImages()420 void MenuBarManager::RequestImages()
421 {
422 
423     m_bRetrieveImages = true;
424     for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
425     {
426         if ( menuItemHandler->xSubMenuManager.is() )
427         {
428             MenuBarManager* pMenuBarManager = static_cast<MenuBarManager*>(menuItemHandler->xSubMenuManager.get());
429             pMenuBarManager->RequestImages();
430         }
431     }
432 }
433 
434 // Helper to reset objects to prepare shutdown
RemoveListener()435 void MenuBarManager::RemoveListener()
436 {
437     SolarMutexGuard g;
438 
439     for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
440     {
441         if ( menuItemHandler->xMenuItemDispatch.is() )
442         {
443             URL aTargetURL;
444             aTargetURL.Complete = menuItemHandler->aMenuItemURL;
445             m_xURLTransformer->parseStrict( aTargetURL );
446 
447             menuItemHandler->xMenuItemDispatch->removeStatusListener(
448                 static_cast< XStatusListener* >( this ), aTargetURL );
449         }
450 
451         menuItemHandler->xMenuItemDispatch.clear();
452 
453         if ( menuItemHandler->xPopupMenu.is() )
454         {
455             {
456                 // Remove popup menu from menu structure
457                 m_pVCLMenu->SetPopupMenu( menuItemHandler->nItemId, nullptr );
458             }
459 
460             Reference< css::lang::XEventListener > xEventListener( menuItemHandler->xPopupMenuController, UNO_QUERY );
461             if ( xEventListener.is() )
462             {
463                 EventObject aEventObject;
464                 aEventObject.Source = static_cast<OWeakObject *>(this);
465                 xEventListener->disposing( aEventObject );
466             }
467 
468             // We now provide a popup menu controller to external code.
469             // Therefore the life-time must be explicitly handled via
470             // dispose!!
471             try
472             {
473                 Reference< XComponent > xComponent( menuItemHandler->xPopupMenuController, UNO_QUERY );
474                 if ( xComponent.is() )
475                     xComponent->dispose();
476             }
477             catch ( const RuntimeException& )
478             {
479                 throw;
480             }
481             catch ( const Exception& )
482             {
483             }
484 
485             // Release references to controller and popup menu
486             menuItemHandler->xPopupMenuController.clear();
487             menuItemHandler->xPopupMenu.clear();
488         }
489 
490         Reference< XComponent > xComponent( menuItemHandler->xSubMenuManager, UNO_QUERY );
491         if ( xComponent.is() )
492             xComponent->dispose();
493     }
494 
495     try
496     {
497         if ( m_xFrame.is() )
498             m_xFrame->removeFrameActionListener( Reference< XFrameActionListener >(
499                                                     static_cast< OWeakObject* >( this ), UNO_QUERY ));
500     }
501     catch ( const Exception& )
502     {
503     }
504 
505     m_xFrame = nullptr;
506 }
507 
disposing(const EventObject & Source)508 void SAL_CALL MenuBarManager::disposing( const EventObject& Source )
509 {
510     MenuItemHandler* pMenuItemDisposing = nullptr;
511 
512     SolarMutexGuard g;
513 
514     for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
515     {
516         if ( menuItemHandler->xMenuItemDispatch.is() &&
517              menuItemHandler->xMenuItemDispatch == Source.Source )
518         {
519             // disposing called from menu item dispatcher, remove listener
520             pMenuItemDisposing = menuItemHandler.get();
521             break;
522         }
523     }
524 
525     if ( pMenuItemDisposing )
526     {
527         // Release references to the dispatch object
528         URL aTargetURL;
529         aTargetURL.Complete = pMenuItemDisposing->aMenuItemURL;
530 
531         m_xURLTransformer->parseStrict( aTargetURL );
532 
533         pMenuItemDisposing->xMenuItemDispatch->removeStatusListener(
534             static_cast< XStatusListener* >( this ), aTargetURL );
535         pMenuItemDisposing->xMenuItemDispatch.clear();
536         if ( pMenuItemDisposing->xPopupMenu.is() )
537         {
538             Reference< css::lang::XEventListener > xEventListener( pMenuItemDisposing->xPopupMenuController, UNO_QUERY );
539             if ( xEventListener.is() )
540                 xEventListener->disposing( Source );
541 
542             {
543                 // Remove popup menu from menu structure as we release our reference to
544                 // the controller.
545                 m_pVCLMenu->SetPopupMenu( pMenuItemDisposing->nItemId, nullptr );
546             }
547 
548             pMenuItemDisposing->xPopupMenuController.clear();
549             pMenuItemDisposing->xPopupMenu.clear();
550         }
551         return;
552     }
553     else if ( Source.Source == m_xFrame )
554     {
555         // Our frame gets disposed. We have to remove all our listeners
556         RemoveListener();
557     }
558     else if ( Source.Source == Reference< XInterface >( m_xDocImageManager, UNO_QUERY ))
559         m_xDocImageManager.clear();
560     else if ( Source.Source == Reference< XInterface >( m_xModuleImageManager, UNO_QUERY ))
561         m_xModuleImageManager.clear();
562 }
563 
lcl_CheckForChildren(Menu * pMenu,sal_uInt16 nItemId)564 static void lcl_CheckForChildren(Menu* pMenu, sal_uInt16 nItemId)
565 {
566     if (PopupMenu* pThisPopup = pMenu->GetPopupMenu( nItemId ))
567         pMenu->EnableItem( nItemId, pThisPopup->GetItemCount() != 0 );
568 }
569 
570 // vcl handler
571 
572 namespace {
573 
574 class QuietInteractionContext:
575     public cppu::WeakImplHelper< css::uno::XCurrentContext >
576 {
577 public:
QuietInteractionContext(css::uno::Reference<css::uno::XCurrentContext> const & context)578     explicit QuietInteractionContext(
579         css::uno::Reference< css::uno::XCurrentContext >
580             const & context):
581         context_(context) {}
582     QuietInteractionContext(const QuietInteractionContext&) = delete;
583     QuietInteractionContext& operator=(const QuietInteractionContext&) = delete;
584 
585 private:
~QuietInteractionContext()586     virtual ~QuietInteractionContext() override {}
587 
getValueByName(OUString const & Name)588     virtual css::uno::Any SAL_CALL getValueByName(
589         OUString const & Name) override
590     {
591         return Name != JAVA_INTERACTION_HANDLER_NAME && context_.is()
592             ? context_->getValueByName(Name)
593             : css::uno::Any();
594     }
595 
596     css::uno::Reference< css::uno::XCurrentContext >
597         context_;
598 };
599 
600 }
601 
IMPL_LINK(MenuBarManager,Activate,Menu *,pMenu,bool)602 IMPL_LINK( MenuBarManager, Activate, Menu *, pMenu, bool )
603 {
604     if ( pMenu == m_pVCLMenu )
605     {
606         css::uno::ContextLayer layer(
607             new QuietInteractionContext(
608                 css::uno::getCurrentContext()));
609 
610         // set/unset hiding disabled menu entries
611         bool bDontHide           = SvtMenuOptions().IsEntryHidingEnabled();
612         const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
613         bool bShowMenuImages     = rSettings.GetUseImagesInMenus();
614         bool bShowShortcuts      = m_bHasMenuBar || rSettings.GetContextMenuShortcuts();
615         bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED );
616 
617         SolarMutexGuard g;
618 
619         MenuFlags nFlag = pMenu->GetMenuFlags();
620         if ( bDontHide )
621             nFlag &= ~MenuFlags::HideDisabledEntries;
622         else
623             nFlag |= MenuFlags::HideDisabledEntries;
624         pMenu->SetMenuFlags( nFlag );
625 
626         if ( m_bActive )
627             return false;
628 
629         m_bActive = true;
630 
631         if ( m_aMenuItemCommand == aSpecialWindowCommand )
632             UpdateSpecialWindowMenu( pMenu, m_xContext );
633 
634         // Check if some modes have changed so we have to update our menu images
635         OUString sIconTheme = SvtMiscOptions().GetIconTheme();
636 
637         if ( m_bRetrieveImages ||
638              bShowMenuImages != m_bShowMenuImages ||
639              sIconTheme != m_sIconTheme )
640         {
641             m_bShowMenuImages   = bShowMenuImages;
642             m_bRetrieveImages   = false;
643             m_sIconTheme     = sIconTheme;
644             FillMenuImages( m_xFrame, pMenu, bShowMenuImages );
645         }
646 
647         // Try to map commands to labels
648         for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
649         {
650             sal_uInt16 nItemId = pMenu->GetItemId( nPos );
651             if (( pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR ) &&
652                 ( pMenu->GetItemText( nItemId ).isEmpty() ))
653             {
654                 OUString aCommand = pMenu->GetItemCommand( nItemId );
655                 if ( !aCommand.isEmpty() ) {
656                     pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aCommand ));
657                 }
658             }
659         }
660 
661         // Try to set accelerator keys
662         {
663             if ( bShowShortcuts )
664                 RetrieveShortcuts( m_aMenuItemHandlerVector );
665 
666             for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
667             {
668                 if ( !bShowShortcuts )
669                 {
670                     pMenu->SetAccelKey( menuItemHandler->nItemId, vcl::KeyCode() );
671                 }
672                 else if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex )
673                 {
674                     // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex
675                     // Only non-popup menu items can have a short-cut
676                     vcl::KeyCode aKeyCode( KEY_F1 );
677                     pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode );
678                 }
679                 else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr )
680                     pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode );
681             }
682         }
683 
684         URL aTargetURL;
685 
686         // Use provided dispatch provider => fallback to frame as dispatch provider
687         Reference< XDispatchProvider > xDispatchProvider;
688         if ( m_xDispatchProvider.is() )
689             xDispatchProvider = m_xDispatchProvider;
690         else
691             xDispatchProvider.set( m_xFrame, UNO_QUERY );
692 
693         if ( xDispatchProvider.is() )
694         {
695             SvtCommandOptions aCmdOptions;
696             for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
697             {
698                 if (menuItemHandler)
699                 {
700                     if ( !menuItemHandler->xMenuItemDispatch.is() &&
701                          !menuItemHandler->xSubMenuManager.is()      )
702                     {
703                         // There is no dispatch mechanism for the special window list menu items,
704                         // because they are handled directly through XFrame->activate!!!
705                         // Don't update dispatches for special file menu items.
706                         if ( !( menuItemHandler->nItemId >= START_ITEMID_WINDOWLIST &&
707                                 menuItemHandler->nItemId < END_ITEMID_WINDOWLIST ) )
708                         {
709                             Reference< XDispatch > xMenuItemDispatch;
710 
711                             aTargetURL.Complete = menuItemHandler->aMenuItemURL;
712 
713                             m_xURLTransformer->parseStrict( aTargetURL );
714 
715                             if ( bHasDisabledEntries )
716                             {
717                                 if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path ))
718                                     pMenu->HideItem( menuItemHandler->nItemId );
719                             }
720 
721                             if ( aTargetURL.Complete.startsWith( ".uno:StyleApply?" ) )
722                                 xMenuItemDispatch = new StyleDispatcher( m_xFrame, m_xURLTransformer, aTargetURL );
723                             else if ( m_bIsBookmarkMenu )
724                                 xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, menuItemHandler->aTargetFrame, 0 );
725                             else
726                                 xMenuItemDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
727 
728                             bool bPopupMenu( false );
729                             if ( !menuItemHandler->xPopupMenuController.is() &&
730                                  m_xPopupMenuControllerFactory->hasController( menuItemHandler->aMenuItemURL, m_aModuleIdentifier ) )
731                             {
732                                 if( xMenuItemDispatch.is() || menuItemHandler->aMenuItemURL != ".uno:RecentFileList" )
733                                     bPopupMenu = CreatePopupMenuController(menuItemHandler.get());
734                             }
735                             else if ( menuItemHandler->xPopupMenuController.is() )
736                             {
737                                 // Force update of popup menu
738                                 menuItemHandler->xPopupMenuController->updatePopupMenu();
739                                 bPopupMenu = true;
740                                 if (PopupMenu*  pThisPopup = pMenu->GetPopupMenu( menuItemHandler->nItemId ))
741                                     pMenu->EnableItem( menuItemHandler->nItemId, pThisPopup->GetItemCount() != 0 );
742                             }
743                             lcl_CheckForChildren(pMenu, menuItemHandler->nItemId);
744 
745                             if ( xMenuItemDispatch.is() )
746                             {
747                                 menuItemHandler->xMenuItemDispatch = xMenuItemDispatch;
748                                 menuItemHandler->aParsedItemURL    = aTargetURL.Complete;
749 
750                                 if ( !bPopupMenu )
751                                 {
752                                     xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
753                                     // For the menubar, we have to keep status listening to support Ubuntu's HUD.
754                                     if ( !m_bHasMenuBar )
755                                         xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
756                                 }
757                             }
758                             else if ( !bPopupMenu )
759                                 pMenu->EnableItem( menuItemHandler->nItemId, false );
760                         }
761                     }
762                     else if ( menuItemHandler->xPopupMenuController.is() )
763                     {
764                         // Force update of popup menu
765                         menuItemHandler->xPopupMenuController->updatePopupMenu();
766                         lcl_CheckForChildren(pMenu, menuItemHandler->nItemId);
767                     }
768                     else if ( menuItemHandler->xMenuItemDispatch.is() )
769                     {
770                         // We need an update to reflect the current state
771                         try
772                         {
773                             aTargetURL.Complete = menuItemHandler->aMenuItemURL;
774                             m_xURLTransformer->parseStrict( aTargetURL );
775 
776                             menuItemHandler->xMenuItemDispatch->addStatusListener(
777                                                                     static_cast< XStatusListener* >( this ), aTargetURL );
778                             menuItemHandler->xMenuItemDispatch->removeStatusListener(
779                                                                     static_cast< XStatusListener* >( this ), aTargetURL );
780                         }
781                         catch ( const Exception& )
782                         {
783                         }
784                     }
785                     else if ( menuItemHandler->xSubMenuManager.is() )
786                         lcl_CheckForChildren(pMenu, menuItemHandler->nItemId);
787                 }
788             }
789         }
790     }
791 
792     return true;
793 }
794 
IMPL_LINK(MenuBarManager,Deactivate,Menu *,pMenu,bool)795 IMPL_LINK( MenuBarManager, Deactivate, Menu *, pMenu, bool )
796 {
797     if ( pMenu == m_pVCLMenu )
798     {
799         m_bActive = false;
800         if ( pMenu->IsMenuBar() && m_xDeferedItemContainer.is() )
801         {
802             // Start timer to handle settings asynchronous
803             // Changing the menu inside this handler leads to
804             // a crash under X!
805             m_aAsyncSettingsTimer.SetInvokeHandler(LINK(this, MenuBarManager, AsyncSettingsHdl));
806             m_aAsyncSettingsTimer.SetTimeout(10);
807             m_aAsyncSettingsTimer.Start();
808         }
809     }
810 
811     return true;
812 }
813 
IMPL_LINK_NOARG(MenuBarManager,AsyncSettingsHdl,Timer *,void)814 IMPL_LINK_NOARG( MenuBarManager, AsyncSettingsHdl, Timer*, void)
815 {
816     SolarMutexGuard g;
817     Reference< XInterface > xSelfHold(
818         static_cast< ::cppu::OWeakObject* >( this ), UNO_QUERY_THROW );
819 
820     m_aAsyncSettingsTimer.Stop();
821     if ( !m_bActive && m_xDeferedItemContainer.is() )
822     {
823         SetItemContainer( m_xDeferedItemContainer );
824         m_xDeferedItemContainer.clear();
825     }
826 }
827 
IMPL_LINK(MenuBarManager,Select,Menu *,pMenu,bool)828 IMPL_LINK( MenuBarManager, Select, Menu *, pMenu, bool )
829 {
830     URL                     aTargetURL;
831     Sequence<PropertyValue> aArgs;
832     Reference< XDispatch >  xDispatch;
833 
834     {
835         SolarMutexGuard g;
836 
837         sal_uInt16 nCurItemId = pMenu->GetCurItemId();
838         sal_uInt16 nCurPos    = pMenu->GetItemPos( nCurItemId );
839         if ( pMenu == m_pVCLMenu &&
840              pMenu->GetItemType( nCurPos ) != MenuItemType::SEPARATOR )
841         {
842             if ( nCurItemId >= START_ITEMID_WINDOWLIST &&
843                  nCurItemId <= END_ITEMID_WINDOWLIST )
844             {
845                 // window list menu item selected
846 
847                 Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
848 
849                 sal_uInt16 nTaskId = START_ITEMID_WINDOWLIST;
850                 Reference< XIndexAccess > xList = xDesktop->getFrames();
851                 sal_Int32 nCount = xList->getCount();
852                 for ( sal_Int32 i=0; i<nCount; ++i )
853                 {
854                     Reference< XFrame > xFrame;
855                     xList->getByIndex(i) >>= xFrame;
856                     if ( xFrame.is() && nTaskId == nCurItemId )
857                     {
858                         VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
859                         pWin->GrabFocus();
860                         pWin->ToTop( ToTopFlags::RestoreWhenMin );
861                         break;
862                     }
863 
864                     nTaskId++;
865                 }
866             }
867             else
868             {
869                 MenuItemHandler* pMenuItemHandler = GetMenuItemHandler( nCurItemId );
870                 if ( pMenuItemHandler && pMenuItemHandler->xMenuItemDispatch.is() )
871                 {
872                     aTargetURL.Complete = pMenuItemHandler->aMenuItemURL;
873                     m_xURLTransformer->parseStrict( aTargetURL );
874 
875                     if ( m_bIsBookmarkMenu )
876                     {
877                         // bookmark menu item selected
878                         aArgs.realloc( 1 );
879                         aArgs[0].Name = "Referer";
880                         aArgs[0].Value <<= OUString( "private:user" );
881                     }
882 
883                     xDispatch = pMenuItemHandler->xMenuItemDispatch;
884                 }
885             }
886         }
887     }
888 
889     // tdf#126054 don't let dispatch destroy this until after function completes
890     rtl::Reference<MenuBarManager> xRef(this);
891     if (xDispatch.is())
892     {
893         SolarMutexReleaser aReleaser;
894         xDispatch->dispatch( aTargetURL, aArgs );
895     }
896 
897     if ( !m_bHasMenuBar )
898         // Standalone (non-native) popup menu doesn't fire deactivate event
899         // in this case, so we have to reset the active flag here.
900         m_bActive = false;
901 
902     return true;
903 }
904 
MustBeHidden(PopupMenu * pPopupMenu,const Reference<XURLTransformer> & rTransformer)905 bool MenuBarManager::MustBeHidden( PopupMenu* pPopupMenu, const Reference< XURLTransformer >& rTransformer )
906 {
907     if ( pPopupMenu )
908     {
909         URL               aTargetURL;
910         SvtCommandOptions aCmdOptions;
911 
912         sal_uInt16 nCount = pPopupMenu->GetItemCount();
913         sal_uInt16 nHideCount( 0 );
914 
915         for ( sal_uInt16 i = 0; i < nCount; i++ )
916         {
917             sal_uInt16 nId = pPopupMenu->GetItemId( i );
918             if ( nId > 0 )
919             {
920                 PopupMenu* pSubPopupMenu = pPopupMenu->GetPopupMenu( nId );
921                 if ( pSubPopupMenu )
922                 {
923                     if ( MustBeHidden( pSubPopupMenu, rTransformer ))
924                     {
925                         pPopupMenu->HideItem( nId );
926                         ++nHideCount;
927                     }
928                 }
929                 else
930                 {
931                     aTargetURL.Complete = pPopupMenu->GetItemCommand( nId );
932                     rTransformer->parseStrict( aTargetURL );
933 
934                     if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aTargetURL.Path ))
935                         ++nHideCount;
936                 }
937             }
938             else
939                 ++nHideCount;
940         }
941 
942         return ( nCount == nHideCount );
943     }
944 
945     return true;
946 }
947 
RetrieveLabelFromCommand(const OUString & rCmdURL)948 OUString MenuBarManager::RetrieveLabelFromCommand(const OUString& rCmdURL)
949 {
950     auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCmdURL, m_aModuleIdentifier);
951     if ( !m_bHasMenuBar )
952     {
953         // This is a context menu, prefer "PopupLabel" over "Label".
954         return vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties);
955     }
956     return vcl::CommandInfoProvider::GetMenuLabelForCommand(aProperties);
957 }
958 
CreatePopupMenuController(MenuItemHandler * pMenuItemHandler)959 bool MenuBarManager::CreatePopupMenuController( MenuItemHandler* pMenuItemHandler )
960 {
961     OUString aItemCommand( pMenuItemHandler->aMenuItemURL );
962 
963     // Try instantiate a popup menu controller. It is stored in the menu item handler.
964     if ( !m_xPopupMenuControllerFactory.is() )
965         return false;
966 
967     Sequence< Any > aSeq( 3 );
968     aSeq[0] <<= comphelper::makePropertyValue( "ModuleIdentifier", m_aModuleIdentifier );
969     aSeq[1] <<= comphelper::makePropertyValue( "Frame", m_xFrame );
970     aSeq[2] <<= comphelper::makePropertyValue( "InToolbar", !m_bHasMenuBar );
971 
972     Reference< XPopupMenuController > xPopupMenuController(
973                                             m_xPopupMenuControllerFactory->createInstanceWithArgumentsAndContext(
974                                                 aItemCommand,
975                                                 aSeq,
976                                                 m_xContext ),
977                                             UNO_QUERY );
978 
979     if ( xPopupMenuController.is() )
980     {
981         // Provide our awt popup menu to the popup menu controller
982         pMenuItemHandler->xPopupMenuController = xPopupMenuController;
983         xPopupMenuController->setPopupMenu( pMenuItemHandler->xPopupMenu );
984         return true;
985     }
986 
987     return false;
988 }
989 
FillMenuManager(Menu * pMenu,const Reference<XFrame> & rFrame,const Reference<XDispatchProvider> & rDispatchProvider,const OUString & rModuleIdentifier,bool bDelete)990 void MenuBarManager::FillMenuManager( Menu* pMenu, const Reference< XFrame >& rFrame,
991                                       const Reference< XDispatchProvider >& rDispatchProvider,
992                                       const OUString& rModuleIdentifier, bool bDelete )
993 {
994     m_xFrame            = rFrame;
995     m_bActive           = false;
996     m_bDeleteMenu       = bDelete;
997     m_pVCLMenu          = pMenu;
998     m_bIsBookmarkMenu   = false;
999     m_xDispatchProvider = rDispatchProvider;
1000 
1001     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
1002     m_bShowMenuImages   = rSettings.GetUseImagesInMenus();
1003     m_bRetrieveImages   = false;
1004 
1005     // Add root as ui configuration listener
1006     RetrieveImageManagers();
1007 
1008     if ( pMenu->IsMenuBar() && rFrame.is() )
1009     {
1010         // First merge all addon popup menus into our structure
1011         sal_uInt16 nPos = 0;
1012         for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
1013         {
1014             sal_uInt16          nItemId  = pMenu->GetItemId( nPos );
1015             OUString aCommand = pMenu->GetItemCommand( nItemId );
1016             if ( aCommand == aSpecialWindowCommand || aCommand == aCmdHelpMenu )
1017             {
1018                 // Retrieve addon popup menus and add them to our menu bar
1019                 framework::AddonMenuManager::MergeAddonPopupMenus( rFrame, nPos, static_cast<MenuBar *>(pMenu) );
1020                 break;
1021             }
1022         }
1023 
1024         // Merge the Add-Ons help menu items into the Office help menu
1025         framework::AddonMenuManager::MergeAddonHelpMenu( rFrame, static_cast<MenuBar *>(pMenu) );
1026     }
1027 
1028     bool    bAccessibilityEnabled( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() );
1029     sal_uInt16 nItemCount = pMenu->GetItemCount();
1030     OUString aItemCommand;
1031     m_aMenuItemHandlerVector.reserve(nItemCount);
1032     for ( sal_uInt16 i = 0; i < nItemCount; i++ )
1033     {
1034         sal_uInt16 nItemId = FillItemCommand(aItemCommand,pMenu, i );
1035 
1036         // Set module identifier when provided from outside
1037         if ( !rModuleIdentifier.isEmpty() )
1038         {
1039             m_aModuleIdentifier = rModuleIdentifier;
1040             m_bModuleIdentified = true;
1041         }
1042 
1043         if (( pMenu->IsMenuBar() || bAccessibilityEnabled ) &&
1044             ( pMenu->GetItemText( nItemId ).isEmpty() ))
1045         {
1046             if ( !aItemCommand.isEmpty() )
1047                 pMenu->SetItemText( nItemId, RetrieveLabelFromCommand( aItemCommand ));
1048         }
1049 
1050         // Command can be just an alias to another command.
1051         auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aItemCommand, m_aModuleIdentifier);
1052         OUString aRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
1053         if ( !aRealCommand.isEmpty() )
1054             aItemCommand = aRealCommand;
1055 
1056         Reference< XDispatch > xDispatch;
1057         Reference< XStatusListener > xStatusListener;
1058         VclPtr<PopupMenu> pPopup = pMenu->GetPopupMenu( nItemId );
1059         // overwrite the show icons on menu option?
1060         MenuItemBits nBits = pMenu->GetItemBits( nItemId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT );
1061         bool bItemShowMenuImages = ( m_bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON;
1062 
1063         if ( pPopup )
1064         {
1065             // Retrieve module identifier from Help Command entry
1066             OUString aModuleIdentifier( rModuleIdentifier );
1067             if (!pMenu->GetHelpCommand(nItemId).isEmpty())
1068             {
1069                 aModuleIdentifier = pMenu->GetHelpCommand( nItemId );
1070                 pMenu->SetHelpCommand( nItemId, "" );
1071             }
1072 
1073             if ( m_xPopupMenuControllerFactory.is() &&
1074                  pPopup->GetItemCount() == 0 &&
1075                  m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier )
1076                   )
1077             {
1078                 // Check if we have to create a popup menu for a uno based popup menu controller.
1079                 // We have to set an empty popup menu into our menu structure so the controller also
1080                 // works with inplace OLE. Remove old dummy popup menu!
1081                 MenuItemHandler* pItemHandler = new MenuItemHandler( nItemId, xStatusListener, xDispatch );
1082                 VCLXPopupMenu* pVCLXPopupMenu = new VCLXPopupMenu;
1083                 PopupMenu* pNewPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
1084                 pMenu->SetPopupMenu( nItemId, pNewPopupMenu );
1085                 pItemHandler->xPopupMenu = pVCLXPopupMenu;
1086                 pItemHandler->aMenuItemURL = aItemCommand;
1087                 m_aMenuItemHandlerVector.push_back( std::unique_ptr<MenuItemHandler>(pItemHandler) );
1088                 pPopup.disposeAndClear();
1089 
1090                 if ( bAccessibilityEnabled )
1091                 {
1092                     if ( CreatePopupMenuController( pItemHandler ))
1093                         pItemHandler->xPopupMenuController->updatePopupMenu();
1094                 }
1095                 lcl_CheckForChildren(pMenu, nItemId);
1096             }
1097             else if ( aItemCommand.startsWith( ADDONSPOPUPMENU_URL_PREFIX_STR ) )
1098             {
1099                 // A special addon popup menu, must be created with a different ctor
1100                 MenuBarManager* pSubMenuManager = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, pPopup, true );
1101                 AddMenu(pSubMenuManager,aItemCommand,nItemId);
1102             }
1103             else
1104             {
1105                 Reference< XDispatchProvider > xPopupMenuDispatchProvider( rDispatchProvider );
1106 
1107                 // Retrieve possible attributes struct
1108                 MenuAttributes* pAttributes = static_cast<MenuAttributes *>(pMenu->GetUserValue( nItemId ));
1109                 if ( pAttributes )
1110                     xPopupMenuDispatchProvider = pAttributes->xDispatchProvider;
1111 
1112                 // Check if this is the help menu. Add menu item if needed
1113                 if ( aItemCommand == aCmdHelpMenu )
1114                 {
1115                 }
1116                 else if ( aItemCommand == aCmdToolsMenu && AddonMenuManager::HasAddonMenuElements() )
1117                 {
1118                     // Create addon popup menu if there exist elements and this is the tools popup menu
1119                     VclPtr<PopupMenu> pSubMenu = AddonMenuManager::CreateAddonMenu(rFrame);
1120                     if ( pSubMenu && ( pSubMenu->GetItemCount() > 0 ))
1121                     {
1122                         if ( pPopup->GetItemType( pPopup->GetItemCount() - 1 ) != MenuItemType::SEPARATOR )
1123                             pPopup->InsertSeparator();
1124 
1125                         pPopup->InsertItem( ITEMID_ADDONLIST, OUString() );
1126                         pPopup->SetPopupMenu( ITEMID_ADDONLIST, pSubMenu );
1127                         pPopup->SetItemCommand( ITEMID_ADDONLIST, ".uno:Addons" );
1128                     }
1129                     else
1130                         pSubMenu.disposeAndClear();
1131                 }
1132 
1133                 MenuBarManager* pSubMenuManager;
1134                 if ( nItemId == ITEMID_ADDONLIST )
1135                     pSubMenuManager = new MenuBarManager( m_xContext, m_xFrame, m_xURLTransformer, pPopup, false );
1136                 else
1137                     pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer,
1138                                                           rDispatchProvider, aModuleIdentifier,
1139                                                           pPopup, false, m_bHasMenuBar );
1140 
1141                 AddMenu(pSubMenuManager, aItemCommand, nItemId);
1142             }
1143         }
1144         else if ( pMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
1145         {
1146             if ( bItemShowMenuImages )
1147                 m_bRetrieveImages = true;
1148 
1149             std::unique_ptr<MenuItemHandler> pItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch ));
1150             pItemHandler->aMenuItemURL = aItemCommand;
1151 
1152             if ( m_xPopupMenuControllerFactory.is() &&
1153                  m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) )
1154             {
1155                 // Check if we have to create a popup menu for a uno based popup menu controller.
1156                 // We have to set an empty popup menu into our menu structure so the controller also
1157                 // works with inplace OLE.
1158                 VCLXPopupMenu* pVCLXPopupMenu = new VCLXPopupMenu;
1159                 PopupMenu* pPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
1160                 pMenu->SetPopupMenu( pItemHandler->nItemId, pPopupMenu );
1161                 pItemHandler->xPopupMenu = pVCLXPopupMenu;
1162 
1163                 if ( bAccessibilityEnabled && CreatePopupMenuController( pItemHandler.get() ) )
1164                 {
1165                     pItemHandler->xPopupMenuController->updatePopupMenu();
1166                 }
1167 
1168                 lcl_CheckForChildren(pMenu, pItemHandler->nItemId);
1169             }
1170 
1171             m_aMenuItemHandlerVector.push_back( std::move(pItemHandler) );
1172         }
1173     }
1174 
1175     if ( m_bHasMenuBar && bAccessibilityEnabled )
1176     {
1177         RetrieveShortcuts( m_aMenuItemHandlerVector );
1178         for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
1179         {
1180             // Set key code, workaround for hard-coded shortcut F1 mapped to .uno:HelpIndex
1181             // Only non-popup menu items can have a short-cut
1182             if ( menuItemHandler->aMenuItemURL == aCmdHelpIndex )
1183             {
1184                 vcl::KeyCode aKeyCode( KEY_F1 );
1185                 pMenu->SetAccelKey( menuItemHandler->nItemId, aKeyCode );
1186             }
1187             else if ( pMenu->GetPopupMenu( menuItemHandler->nItemId ) == nullptr )
1188                 pMenu->SetAccelKey( menuItemHandler->nItemId, menuItemHandler->aKeyCode );
1189         }
1190     }
1191 
1192     SetHdl();
1193 }
1194 
impl_RetrieveShortcutsFromConfiguration(const Reference<XAcceleratorConfiguration> & rAccelCfg,const Sequence<OUString> & rCommands,std::vector<std::unique_ptr<MenuItemHandler>> & aMenuShortCuts)1195 void MenuBarManager::impl_RetrieveShortcutsFromConfiguration(
1196     const Reference< XAcceleratorConfiguration >& rAccelCfg,
1197     const Sequence< OUString >& rCommands,
1198     std::vector< std::unique_ptr<MenuItemHandler> >& aMenuShortCuts )
1199 {
1200     if ( rAccelCfg.is() )
1201     {
1202         try
1203         {
1204             css::awt::KeyEvent aKeyEvent;
1205             Sequence< Any > aSeqKeyCode = rAccelCfg->getPreferredKeyEventsForCommandList( rCommands );
1206             for ( sal_Int32 i = 0; i < aSeqKeyCode.getLength(); i++ )
1207             {
1208                 if ( aSeqKeyCode[i] >>= aKeyEvent )
1209                     aMenuShortCuts[i]->aKeyCode = svt::AcceleratorExecute::st_AWTKey2VCLKey( aKeyEvent );
1210             }
1211         }
1212         catch ( const IllegalArgumentException& )
1213         {
1214         }
1215     }
1216 }
1217 
RetrieveShortcuts(std::vector<std::unique_ptr<MenuItemHandler>> & aMenuShortCuts)1218 void MenuBarManager::RetrieveShortcuts( std::vector< std::unique_ptr<MenuItemHandler> >& aMenuShortCuts )
1219 {
1220     if ( !m_bModuleIdentified )
1221     {
1222         m_bModuleIdentified = true;
1223         Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
1224 
1225         try
1226         {
1227             m_aModuleIdentifier = xModuleManager->identify( m_xFrame );
1228         }
1229         catch( const Exception& )
1230         {
1231         }
1232     }
1233 
1234     if ( m_bModuleIdentified )
1235     {
1236         Reference< XAcceleratorConfiguration > xDocAccelCfg( m_xDocAcceleratorManager );
1237         Reference< XAcceleratorConfiguration > xModuleAccelCfg( m_xModuleAcceleratorManager );
1238         Reference< XAcceleratorConfiguration > xGlobalAccelCfg( m_xGlobalAcceleratorManager );
1239 
1240         if ( !m_bAcceleratorCfg )
1241         {
1242             // Retrieve references on demand
1243             m_bAcceleratorCfg = true;
1244             if ( !xDocAccelCfg.is() )
1245             {
1246                 Reference< XController > xController = m_xFrame->getController();
1247                 Reference< XModel > xModel;
1248                 if ( xController.is() )
1249                 {
1250                     xModel = xController->getModel();
1251                     if ( xModel.is() )
1252                     {
1253                         Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY );
1254                         if ( xSupplier.is() )
1255                         {
1256                             Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager();
1257                             if ( xDocUICfgMgr.is() )
1258                             {
1259                                 xDocAccelCfg = xDocUICfgMgr->getShortCutManager();
1260                                 m_xDocAcceleratorManager = xDocAccelCfg;
1261                             }
1262                         }
1263                     }
1264                 }
1265             }
1266 
1267             if ( !xModuleAccelCfg.is() )
1268             {
1269                 Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier =
1270                     theModuleUIConfigurationManagerSupplier::get( m_xContext );
1271                 try
1272                 {
1273                     Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier );
1274                     if ( xUICfgMgr.is() )
1275                     {
1276                         xModuleAccelCfg = xUICfgMgr->getShortCutManager();
1277                         m_xModuleAcceleratorManager = xModuleAccelCfg;
1278                     }
1279                 }
1280                 catch ( const RuntimeException& )
1281                 {
1282                     throw;
1283                 }
1284                 catch ( const Exception& )
1285                 {
1286                 }
1287             }
1288 
1289             if ( !xGlobalAccelCfg.is() ) try
1290             {
1291                 xGlobalAccelCfg = GlobalAcceleratorConfiguration::create( m_xContext );
1292                 m_xGlobalAcceleratorManager = xGlobalAccelCfg;
1293             }
1294             catch ( const css::uno::DeploymentException& )
1295             {
1296                 SAL_WARN("fwk.uielement", "GlobalAcceleratorConfiguration"
1297                         " not available. This should happen only on mobile platforms.");
1298             }
1299         }
1300 
1301         vcl::KeyCode aEmptyKeyCode;
1302         Sequence< OUString > aSeq( aMenuShortCuts.size() );
1303         const sal_uInt32 nCount = aMenuShortCuts.size();
1304         for ( sal_uInt32 i = 0; i < nCount; ++i )
1305         {
1306             OUString aItemURL = aMenuShortCuts[i]->aMenuItemURL;
1307             if( aItemURL.isEmpty() && aMenuShortCuts[i]->xSubMenuManager.is())
1308                 aItemURL = "-"; // tdf#99527 prevent throw in case of empty commands
1309             aSeq[i] = aItemURL;
1310             aMenuShortCuts[i]->aKeyCode = aEmptyKeyCode;
1311         }
1312 
1313         if ( m_xGlobalAcceleratorManager.is() )
1314             impl_RetrieveShortcutsFromConfiguration( xGlobalAccelCfg, aSeq, aMenuShortCuts );
1315         if ( m_xModuleAcceleratorManager.is() )
1316             impl_RetrieveShortcutsFromConfiguration( xModuleAccelCfg, aSeq, aMenuShortCuts );
1317         if ( m_xDocAcceleratorManager.is() )
1318             impl_RetrieveShortcutsFromConfiguration( xDocAccelCfg, aSeq, aMenuShortCuts );
1319     }
1320 }
1321 
RetrieveImageManagers()1322 void MenuBarManager::RetrieveImageManagers()
1323 {
1324     if ( !m_xDocImageManager.is() )
1325     {
1326         Reference< XController > xController = m_xFrame->getController();
1327         Reference< XModel > xModel;
1328         if ( xController.is() )
1329         {
1330             xModel = xController->getModel();
1331             if ( xModel.is() )
1332             {
1333                 Reference< XUIConfigurationManagerSupplier > xSupplier( xModel, UNO_QUERY );
1334                 if ( xSupplier.is() )
1335                 {
1336                     Reference< XUIConfigurationManager > xDocUICfgMgr = xSupplier->getUIConfigurationManager();
1337                     m_xDocImageManager.set( xDocUICfgMgr->getImageManager(), UNO_QUERY );
1338                     m_xDocImageManager->addConfigurationListener(
1339                                             Reference< XUIConfigurationListener >(
1340                                                 static_cast< OWeakObject* >( this ), UNO_QUERY ));
1341                 }
1342             }
1343         }
1344     }
1345 
1346     Reference< XModuleManager2 > xModuleManager;
1347     if ( m_aModuleIdentifier.isEmpty() )
1348         xModuleManager.set( ModuleManager::create( m_xContext ) );
1349 
1350     try
1351     {
1352         if ( xModuleManager.is() )
1353             m_aModuleIdentifier = xModuleManager->identify( Reference< XInterface >( m_xFrame, UNO_QUERY ) );
1354     }
1355     catch( const Exception& )
1356     {
1357     }
1358 
1359     if ( !m_xModuleImageManager.is() )
1360     {
1361         Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier =
1362             theModuleUIConfigurationManagerSupplier::get( m_xContext );
1363         Reference< XUIConfigurationManager > xUICfgMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( m_aModuleIdentifier );
1364         m_xModuleImageManager.set( xUICfgMgr->getImageManager(), UNO_QUERY );
1365         m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(
1366                                                             static_cast< OWeakObject* >( this ), UNO_QUERY ));
1367     }
1368 }
1369 
FillMenuWithConfiguration(sal_uInt16 & nId,Menu * pMenu,const OUString & rModuleIdentifier,const Reference<XIndexAccess> & rItemContainer,const Reference<XURLTransformer> & rTransformer)1370 void MenuBarManager::FillMenuWithConfiguration(
1371     sal_uInt16&                         nId,
1372     Menu*                               pMenu,
1373     const OUString&              rModuleIdentifier,
1374     const Reference< XIndexAccess >&    rItemContainer,
1375     const Reference< XURLTransformer >& rTransformer )
1376 {
1377     Reference< XDispatchProvider > xEmptyDispatchProvider;
1378     MenuBarManager::FillMenu( nId, pMenu, rModuleIdentifier, rItemContainer, xEmptyDispatchProvider );
1379 
1380     // Merge add-on menu entries into the menu bar
1381     MenuBarManager::MergeAddonMenus( pMenu,
1382                                      AddonsOptions().GetMergeMenuInstructions(),
1383                                      rModuleIdentifier );
1384 
1385     bool bHasDisabledEntries = SvtCommandOptions().HasEntries( SvtCommandOptions::CMDOPTION_DISABLED );
1386     if ( bHasDisabledEntries )
1387     {
1388         sal_uInt16 nCount = pMenu->GetItemCount();
1389         for ( sal_uInt16 i = 0; i < nCount; i++ )
1390         {
1391             sal_uInt16 nID = pMenu->GetItemId( i );
1392             if ( nID > 0 )
1393             {
1394                 PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nID );
1395                 if ( pPopupMenu )
1396                 {
1397                     if ( MustBeHidden( pPopupMenu, rTransformer ))
1398                         pMenu->HideItem( nId );
1399                 }
1400             }
1401         }
1402     }
1403 }
1404 
FillMenu(sal_uInt16 & nId,Menu * pMenu,const OUString & rModuleIdentifier,const Reference<XIndexAccess> & rItemContainer,const Reference<XDispatchProvider> & rDispatchProvider)1405 void MenuBarManager::FillMenu(
1406     sal_uInt16& nId,
1407     Menu* pMenu,
1408     const OUString& rModuleIdentifier,
1409     const Reference< XIndexAccess >& rItemContainer,
1410     const Reference< XDispatchProvider >& rDispatchProvider )
1411 {
1412     // Fill menu bar with container contents
1413     for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ )
1414     {
1415         Sequence< PropertyValue > aProp;
1416         OUString aCommandURL;
1417         OUString aLabel;
1418         OUString aModuleIdentifier( rModuleIdentifier );
1419         sal_uInt16 nType = 0;
1420         Reference< XIndexAccess > xIndexContainer;
1421         Reference< XDispatchProvider > xDispatchProvider( rDispatchProvider );
1422         sal_Int16 nStyle = 0;
1423         try
1424         {
1425             if ( rItemContainer->getByIndex( n ) >>= aProp )
1426             {
1427                 bool bShow = true;
1428                 bool bEnabled = true;
1429 
1430                 for ( int i = 0; i < aProp.getLength(); i++ )
1431                 {
1432                     OUString aPropName = aProp[i].Name;
1433                     if ( aPropName == "CommandURL" )
1434                         aProp[i].Value >>= aCommandURL;
1435                     else if ( aPropName == "ItemDescriptorContainer" )
1436                         aProp[i].Value >>= xIndexContainer;
1437                     else if ( aPropName == "Label" )
1438                         aProp[i].Value >>= aLabel;
1439                     else if ( aPropName == "Type" )
1440                         aProp[i].Value >>= nType;
1441                     else if ( aPropName == "ModuleIdentifier" )
1442                         aProp[i].Value >>= aModuleIdentifier;
1443                     else if ( aPropName == "DispatchProvider" )
1444                         aProp[i].Value >>= xDispatchProvider;
1445                     else if ( aPropName == "Style" )
1446                         aProp[i].Value >>= nStyle;
1447                     else if ( aPropName == "IsVisible" )
1448                         aProp[i].Value >>= bShow;
1449                     else if ( aPropName == "Enabled" )
1450                         aProp[i].Value >>= bEnabled;
1451                 }
1452 
1453                 if (!aCommandURL.isEmpty() && vcl::CommandInfoProvider::IsExperimental(aCommandURL, rModuleIdentifier) &&
1454                     !SvtMiscOptions().IsExperimentalMode())
1455                 {
1456                     continue;
1457                 }
1458 
1459                 if ( nType == css::ui::ItemType::DEFAULT )
1460                 {
1461                     pMenu->InsertItem( nId, aLabel );
1462                     pMenu->SetItemCommand( nId, aCommandURL );
1463 
1464                     if ( nStyle )
1465                     {
1466                         MenuItemBits nBits = pMenu->GetItemBits( nId );
1467                         if ( nStyle & css::ui::ItemStyle::ICON )
1468                            nBits |= MenuItemBits::ICON;
1469                         if ( nStyle & css::ui::ItemStyle::TEXT )
1470                            nBits |= MenuItemBits::TEXT;
1471                         if ( nStyle & css::ui::ItemStyle::RADIO_CHECK )
1472                            nBits |= MenuItemBits::RADIOCHECK;
1473                         pMenu->SetItemBits( nId, nBits );
1474                     }
1475 
1476                     if ( !bShow )
1477                         pMenu->HideItem( nId );
1478 
1479                     if ( !bEnabled)
1480                         pMenu->EnableItem( nId, false );
1481 
1482                     if ( xIndexContainer.is() )
1483                     {
1484                         VclPtr<PopupMenu> pNewPopupMenu = VclPtr<PopupMenu>::Create();
1485                         pMenu->SetPopupMenu( nId, pNewPopupMenu );
1486 
1487                         if ( xDispatchProvider.is() )
1488                         {
1489                             // Use attributes struct to transport special dispatch provider
1490                             void* nAttributePtr = MenuAttributes::CreateAttribute(xDispatchProvider);
1491                             pMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute);
1492                         }
1493 
1494                         // Use help command to transport module identifier
1495                         if ( !aModuleIdentifier.isEmpty() )
1496                             pMenu->SetHelpCommand( nId, aModuleIdentifier );
1497 
1498                         ++nId;
1499                         FillMenu( nId, pNewPopupMenu, aModuleIdentifier, xIndexContainer, xDispatchProvider );
1500                     }
1501                     else
1502                         ++nId;
1503                 }
1504                 else
1505                 {
1506                     pMenu->InsertSeparator();
1507                     ++nId;
1508                 }
1509             }
1510         }
1511         catch ( const IndexOutOfBoundsException& )
1512         {
1513             break;
1514         }
1515     }
1516 }
1517 
MergeAddonMenus(Menu * pMenuBar,const MergeMenuInstructionContainer & aMergeInstructionContainer,const OUString & rModuleIdentifier)1518 void MenuBarManager::MergeAddonMenus(
1519     Menu* pMenuBar,
1520     const MergeMenuInstructionContainer& aMergeInstructionContainer,
1521     const OUString& rModuleIdentifier )
1522 {
1523     // set start value for the item ID for the new addon menu items
1524     sal_uInt16 nItemId = ADDONMENU_MERGE_ITEMID_START;
1525 
1526     const sal_uInt32 nCount = aMergeInstructionContainer.size();
1527     for ( sal_uInt32 i = 0; i < nCount; i++ )
1528     {
1529         const MergeMenuInstruction& rMergeInstruction = aMergeInstructionContainer[i];
1530 
1531         if ( MenuBarMerger::IsCorrectContext( rMergeInstruction.aMergeContext, rModuleIdentifier ))
1532         {
1533             ::std::vector< OUString > aMergePath;
1534 
1535             // retrieve the merge path from the merge point string
1536             MenuBarMerger::RetrieveReferencePath( rMergeInstruction.aMergePoint, aMergePath );
1537 
1538             // convert the sequence/sequence property value to a more convenient vector<>
1539             AddonMenuContainer aMergeMenuItems;
1540             MenuBarMerger::GetSubMenu( rMergeInstruction.aMergeMenu, aMergeMenuItems );
1541 
1542             // try to find the reference point for our merge operation
1543             Menu* pMenu = pMenuBar;
1544             ReferencePathInfo aResult = MenuBarMerger::FindReferencePath( aMergePath, pMenu );
1545 
1546             if ( aResult.eResult == RP_OK )
1547             {
1548                 // normal merge operation
1549                 MenuBarMerger::ProcessMergeOperation( aResult.pPopupMenu,
1550                                                       aResult.nPos,
1551                                                       nItemId,
1552                                                       rMergeInstruction.aMergeCommand,
1553                                                       rMergeInstruction.aMergeCommandParameter,
1554                                                       rModuleIdentifier,
1555                                                       aMergeMenuItems );
1556             }
1557             else
1558             {
1559                 // fallback
1560                 MenuBarMerger::ProcessFallbackOperation( aResult,
1561                                                          nItemId,
1562                                                          rMergeInstruction.aMergeCommand,
1563                                                          rMergeInstruction.aMergeFallback,
1564                                                          aMergePath,
1565                                                          rModuleIdentifier,
1566                                                          aMergeMenuItems );
1567             }
1568         }
1569     }
1570 }
1571 
SetItemContainer(const Reference<XIndexAccess> & rItemContainer)1572 void MenuBarManager::SetItemContainer( const Reference< XIndexAccess >& rItemContainer )
1573 {
1574     SolarMutexGuard aSolarMutexGuard;
1575 
1576     Reference< XFrame > xFrame = m_xFrame;
1577 
1578     if ( !m_bModuleIdentified )
1579     {
1580         m_bModuleIdentified = true;
1581         Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
1582 
1583         try
1584         {
1585             m_aModuleIdentifier = xModuleManager->identify( xFrame );
1586         }
1587         catch( const Exception& )
1588         {
1589         }
1590     }
1591 
1592     // Clear MenuBarManager structures
1593     {
1594         // Check active state as we cannot change our VCL menu during activation by the user
1595         if ( m_bActive )
1596         {
1597             m_xDeferedItemContainer = rItemContainer;
1598             return;
1599         }
1600 
1601         RemoveListener();
1602         m_aMenuItemHandlerVector.clear();
1603         m_pVCLMenu->Clear();
1604 
1605         sal_uInt16          nId = 1;
1606 
1607         // Fill menu bar with container contents
1608         FillMenuWithConfiguration( nId, m_pVCLMenu, m_aModuleIdentifier, rItemContainer, m_xURLTransformer );
1609 
1610         // Refill menu manager again
1611         Reference< XDispatchProvider > xDispatchProvider;
1612         FillMenuManager( m_pVCLMenu, xFrame, xDispatchProvider, m_aModuleIdentifier, false );
1613 
1614         // add itself as frame action listener
1615         m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( static_cast< OWeakObject* >( this ), UNO_QUERY ));
1616     }
1617 }
1618 
GetPopupController(PopupControllerCache & rPopupController)1619 void MenuBarManager::GetPopupController( PopupControllerCache& rPopupController )
1620 {
1621 
1622     SolarMutexGuard aSolarMutexGuard;
1623 
1624     for (auto const& menuItemHandler : m_aMenuItemHandlerVector)
1625     {
1626         if ( menuItemHandler->xPopupMenuController.is() )
1627         {
1628             Reference< XDispatchProvider > xDispatchProvider( menuItemHandler->xPopupMenuController, UNO_QUERY );
1629 
1630             PopupControllerEntry aPopupControllerEntry;
1631             aPopupControllerEntry.m_xDispatchProvider = xDispatchProvider;
1632 
1633             // Just use the main part of the URL for popup menu controllers
1634             sal_Int32     nQueryPart( 0 );
1635             sal_Int32     nSchemePart( 0 );
1636             OUString aMainURL( "vnd.sun.star.popup:" );
1637             OUString aMenuURL( menuItemHandler->aMenuItemURL );
1638 
1639             nSchemePart = aMenuURL.indexOf( ':' );
1640             if (( nSchemePart > 0 ) &&
1641                 ( aMenuURL.getLength() > ( nSchemePart+1 )))
1642             {
1643                 nQueryPart  = aMenuURL.indexOf( '?', nSchemePart );
1644                 if ( nQueryPart > 0 )
1645                     aMainURL += aMenuURL.copy( nSchemePart, nQueryPart-nSchemePart );
1646                 else if ( nQueryPart == -1 )
1647                     aMainURL += aMenuURL.copy( nSchemePart+1 );
1648 
1649                 rPopupController.emplace( aMainURL, aPopupControllerEntry );
1650             }
1651         }
1652         if ( menuItemHandler->xSubMenuManager.is() )
1653         {
1654             MenuBarManager* pMenuBarManager = static_cast<MenuBarManager*>(menuItemHandler->xSubMenuManager.get());
1655             if ( pMenuBarManager )
1656                 pMenuBarManager->GetPopupController( rPopupController );
1657         }
1658     }
1659 }
1660 
AddMenu(MenuBarManager * pSubMenuManager,const OUString & _sItemCommand,sal_uInt16 _nItemId)1661 void MenuBarManager::AddMenu(MenuBarManager* pSubMenuManager,const OUString& _sItemCommand,sal_uInt16 _nItemId)
1662 {
1663     Reference< XStatusListener > xSubMenuManager( static_cast< OWeakObject *>( pSubMenuManager ), UNO_QUERY );
1664     m_xFrame->addFrameActionListener( Reference< XFrameActionListener >( xSubMenuManager, UNO_QUERY ));
1665 
1666     // store menu item command as we later have to know which menu is active (see Activate handler)
1667     pSubMenuManager->m_aMenuItemCommand = _sItemCommand;
1668     Reference< XDispatch > xDispatch;
1669     std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler(
1670                                                 _nItemId,
1671                                                 xSubMenuManager,
1672                                                 xDispatch ));
1673     pMenuItemHandler->aMenuItemURL = _sItemCommand;
1674     m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) );
1675 }
1676 
FillItemCommand(OUString & _rItemCommand,Menu * _pMenu,sal_uInt16 _nIndex) const1677 sal_uInt16 MenuBarManager::FillItemCommand(OUString& _rItemCommand, Menu* _pMenu,sal_uInt16 _nIndex) const
1678 {
1679     sal_uInt16 nItemId = _pMenu->GetItemId( _nIndex );
1680 
1681     _rItemCommand = _pMenu->GetItemCommand( nItemId );
1682     if ( _rItemCommand.isEmpty() )
1683     {
1684         _rItemCommand = "slot:" + OUString::number( nItemId );
1685         _pMenu->SetItemCommand( nItemId, _rItemCommand );
1686     }
1687     return nItemId;
1688 }
Init(const Reference<XFrame> & rFrame,Menu * pAddonMenu,bool _bHandlePopUp)1689 void MenuBarManager::Init(const Reference< XFrame >& rFrame, Menu* pAddonMenu, bool _bHandlePopUp)
1690 {
1691     m_bActive           = false;
1692     m_bDeleteMenu       = false;
1693     m_pVCLMenu          = pAddonMenu;
1694     m_xFrame            = rFrame;
1695     m_bIsBookmarkMenu   = true;
1696     m_bShowMenuImages   = true;
1697 
1698     m_xPopupMenuControllerFactory = frame::thePopupMenuControllerFactory::get(
1699         ::comphelper::getProcessComponentContext());
1700 
1701     Reference< XStatusListener > xStatusListener;
1702     Reference< XDispatch > xDispatch;
1703     sal_uInt16 nItemCount = pAddonMenu->GetItemCount();
1704     OUString aItemCommand;
1705     m_aMenuItemHandlerVector.reserve(nItemCount);
1706     for ( sal_uInt16 i = 0; i < nItemCount; i++ )
1707     {
1708         sal_uInt16 nItemId = FillItemCommand(aItemCommand,pAddonMenu, i );
1709 
1710         PopupMenu* pPopupMenu = pAddonMenu->GetPopupMenu( nItemId );
1711         if ( pPopupMenu )
1712         {
1713             Reference< XDispatchProvider > xDispatchProvider;
1714             MenuBarManager* pSubMenuManager = new MenuBarManager( m_xContext, rFrame, m_xURLTransformer,
1715                                                                   xDispatchProvider, OUString(), pPopupMenu,
1716                                                                   false );
1717 
1718             Reference< XStatusListener > xSubMenuManager( static_cast< OWeakObject *>( pSubMenuManager ), UNO_QUERY );
1719 
1720             // store menu item command as we later have to know which menu is active (see Activate handler)
1721             pSubMenuManager->m_aMenuItemCommand = aItemCommand;
1722 
1723             std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler(
1724                                                         nItemId,
1725                                                         xSubMenuManager,
1726                                                         xDispatch ));
1727             m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) );
1728         }
1729         else
1730         {
1731             if ( pAddonMenu->GetItemType( i ) != MenuItemType::SEPARATOR )
1732             {
1733                 MenuAttributes* pAddonAttributes = static_cast<MenuAttributes *>(pAddonMenu->GetUserValue( nItemId ));
1734                 std::unique_ptr<MenuItemHandler> pMenuItemHandler(new MenuItemHandler( nItemId, xStatusListener, xDispatch ));
1735 
1736                 if ( pAddonAttributes )
1737                 {
1738                     // read additional attributes from attributes struct and AddonMenu implementation
1739                     // will delete all attributes itself!!
1740                     pMenuItemHandler->aTargetFrame = pAddonAttributes->aTargetFrame;
1741                 }
1742 
1743                 pMenuItemHandler->aMenuItemURL = aItemCommand;
1744                 if ( _bHandlePopUp )
1745                 {
1746                     // Check if we have to create a popup menu for a uno based popup menu controller.
1747                     // We have to set an empty popup menu into our menu structure so the controller also
1748                     // works with inplace OLE.
1749                     if ( m_xPopupMenuControllerFactory.is() &&
1750                          m_xPopupMenuControllerFactory->hasController( aItemCommand, m_aModuleIdentifier ) )
1751                     {
1752                         VCLXPopupMenu* pVCLXPopupMenu = new VCLXPopupMenu;
1753                         PopupMenu* pCtlPopupMenu = static_cast<PopupMenu *>(pVCLXPopupMenu->GetMenu());
1754                         pAddonMenu->SetPopupMenu( pMenuItemHandler->nItemId, pCtlPopupMenu );
1755                         pMenuItemHandler->xPopupMenu = pVCLXPopupMenu;
1756 
1757                     }
1758                 }
1759                 m_aMenuItemHandlerVector.push_back( std::move(pMenuItemHandler) );
1760             }
1761         }
1762     }
1763 
1764     SetHdl();
1765 }
1766 
SetHdl()1767 void MenuBarManager::SetHdl()
1768 {
1769     m_pVCLMenu->SetActivateHdl( LINK( this, MenuBarManager, Activate ));
1770     m_pVCLMenu->SetDeactivateHdl( LINK( this, MenuBarManager, Deactivate ));
1771     m_pVCLMenu->SetSelectHdl( LINK( this, MenuBarManager, Select ));
1772 
1773     if ( !m_xURLTransformer.is() && m_xContext.is() )
1774         m_xURLTransformer.set( URLTransformer::create( m_xContext) );
1775 }
1776 
UpdateSpecialWindowMenu(Menu * pMenu,const Reference<XComponentContext> & xContext)1777 void MenuBarManager::UpdateSpecialWindowMenu( Menu* pMenu,const Reference< XComponentContext >& xContext )
1778 {
1779     // update window list
1780     ::std::vector< OUString > aNewWindowListVector;
1781 
1782     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
1783 
1784     sal_uInt16  nActiveItemId = 0;
1785     sal_uInt16  nItemId = START_ITEMID_WINDOWLIST;
1786 
1787     Reference< XFrame > xCurrentFrame = xDesktop->getCurrentFrame();
1788     Reference< XIndexAccess > xList = xDesktop->getFrames();
1789     sal_Int32 nFrameCount = xList->getCount();
1790     aNewWindowListVector.reserve(nFrameCount);
1791     for (sal_Int32 i=0; i<nFrameCount; ++i )
1792     {
1793         Reference< XFrame > xFrame;
1794         xList->getByIndex(i) >>= xFrame;
1795 
1796         if (xFrame.is())
1797         {
1798             if ( xFrame == xCurrentFrame )
1799                 nActiveItemId = nItemId;
1800 
1801             VclPtr<vcl::Window> pWin = VCLUnoHelper::GetWindow( xFrame->getContainerWindow() );
1802             OUString sWindowTitle;
1803             if ( pWin && pWin->IsVisible() )
1804                 sWindowTitle = pWin->GetText();
1805 
1806             // tdf#101658 In case the frame is embedded somewhere, LO has no control over it.
1807             // So we just skip it.
1808             if ( sWindowTitle.isEmpty() )
1809                 continue;
1810 
1811             aNewWindowListVector.push_back( sWindowTitle );
1812             ++nItemId;
1813         }
1814     }
1815 
1816     {
1817         SolarMutexGuard g;
1818 
1819         int nItemCount = pMenu->GetItemCount();
1820 
1821         if ( nItemCount > 0 )
1822         {
1823             // remove all old window list entries from menu
1824             sal_uInt16 nPos = pMenu->GetItemPos( START_ITEMID_WINDOWLIST );
1825             for ( sal_uInt16 n = nPos; n < pMenu->GetItemCount(); )
1826                 pMenu->RemoveItem( n );
1827 
1828             if ( pMenu->GetItemType( pMenu->GetItemCount()-1 ) == MenuItemType::SEPARATOR )
1829                 pMenu->RemoveItem( pMenu->GetItemCount()-1 );
1830         }
1831 
1832         if ( !aNewWindowListVector.empty() )
1833         {
1834             // append new window list entries to menu
1835             pMenu->InsertSeparator();
1836             nItemId = START_ITEMID_WINDOWLIST;
1837             const sal_uInt32 nCount = aNewWindowListVector.size();
1838             for ( sal_uInt32 i = 0; i < nCount; i++ )
1839             {
1840                 pMenu->InsertItem( nItemId, aNewWindowListVector.at( i ), MenuItemBits::RADIOCHECK );
1841                 if ( nItemId == nActiveItemId )
1842                     pMenu->CheckItem( nItemId );
1843                 ++nItemId;
1844             }
1845         }
1846     }
1847 }
1848 
FillMenuImages(Reference<XFrame> const & _xFrame,Menu * _pMenu,bool bShowMenuImages)1849 void MenuBarManager::FillMenuImages(Reference< XFrame > const & _xFrame, Menu* _pMenu,bool bShowMenuImages)
1850 {
1851     AddonsOptions aAddonOptions;
1852 
1853     for ( sal_uInt16 nPos = 0; nPos < _pMenu->GetItemCount(); nPos++ )
1854     {
1855         sal_uInt16 nId = _pMenu->GetItemId( nPos );
1856         if ( _pMenu->GetItemType( nPos ) != MenuItemType::SEPARATOR )
1857         {
1858             // overwrite the show icons on menu option?
1859             MenuItemBits nBits = _pMenu->GetItemBits( nId ) & ( MenuItemBits::ICON | MenuItemBits::TEXT );
1860             bool bTmpShowMenuImages = ( bShowMenuImages && nBits != MenuItemBits::TEXT ) || nBits & MenuItemBits::ICON;
1861 
1862             if ( bTmpShowMenuImages )
1863             {
1864                 bool        bImageSet = false;
1865                 OUString aImageId;
1866 
1867                 ::framework::MenuAttributes* pMenuAttributes =
1868                     static_cast< ::framework::MenuAttributes*>(_pMenu->GetUserValue( nId ));
1869 
1870                 if ( pMenuAttributes )
1871                     aImageId = pMenuAttributes->aImageId; // Retrieve image id from menu attributes
1872 
1873                 if ( !aImageId.isEmpty() )
1874                 {
1875                     Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aImageId, _xFrame);
1876                     if ( !!aImage )
1877                     {
1878                         bImageSet = true;
1879                         _pMenu->SetItemImage( nId, aImage );
1880                     }
1881                 }
1882 
1883                 if ( !bImageSet )
1884                 {
1885                     OUString aMenuItemCommand = _pMenu->GetItemCommand( nId );
1886                     Image aImage = vcl::CommandInfoProvider::GetImageForCommand(aMenuItemCommand, _xFrame);
1887                     if ( !aImage )
1888                         aImage = aAddonOptions.GetImageFromURL( aMenuItemCommand, false );
1889 
1890                     _pMenu->SetItemImage( nId, aImage );
1891                 }
1892             }
1893             else
1894                 _pMenu->SetItemImage( nId, Image() );
1895         }
1896     }
1897 }
1898 
1899 }
1900 
1901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1902