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/toolbarsmenucontroller.hxx>
21 
22 #include <algorithm>
23 #include <string_view>
24 
25 #include <services.h>
26 #include <strings.hrc>
27 #include <classes/fwkresid.hxx>
28 #include <framework/sfxhelperfunctions.hxx>
29 #include <uiconfiguration/windowstateproperties.hxx>
30 
31 #include <com/sun/star/awt/XDevice.hpp>
32 #include <com/sun/star/beans/PropertyValue.hpp>
33 #include <com/sun/star/awt/MenuItemStyle.hpp>
34 #include <com/sun/star/frame/ModuleManager.hpp>
35 #include <com/sun/star/frame/XDispatchProvider.hpp>
36 #include <com/sun/star/util/XURLTransformer.hpp>
37 #include <com/sun/star/container/XNameContainer.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/frame/XLayoutManager.hpp>
40 #include <com/sun/star/ui/XUIElementSettings.hpp>
41 #include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
42 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
43 #include <com/sun/star/ui/UIElementType.hpp>
44 #include <com/sun/star/ui/theWindowStateConfiguration.hpp>
45 
46 #include <vcl/menu.hxx>
47 #include <vcl/svapp.hxx>
48 #include <vcl/i18nhelp.hxx>
49 #include <vcl/image.hxx>
50 #include <vcl/settings.hxx>
51 #include <vcl/commandinfoprovider.hxx>
52 #include <rtl/ustrbuf.hxx>
53 #include <sal/log.hxx>
54 #include <toolkit/helper/vclunohelper.hxx>
55 #include <vcl/window.hxx>
56 #include <svtools/menuoptions.hxx>
57 #include <unotools/cmdoptions.hxx>
58 #include <svtools/miscopt.hxx>
59 #include <unotools/collatorwrapper.hxx>
60 #include <unotools/syslocale.hxx>
61 
62 //  Defines
63 
64 using namespace ::com::sun::star;
65 using namespace ::com::sun::star::uno;
66 using namespace ::com::sun::star::lang;
67 using namespace ::com::sun::star::frame;
68 using namespace ::com::sun::star::beans;
69 using namespace ::com::sun::star::util;
70 using namespace ::com::sun::star::container;
71 using namespace ::com::sun::star::ui;
72 
73 static const char CMD_RESTOREVISIBILITY[]       = ".cmd:RestoreVisibility";
74 
75 static const char STATIC_CMD_PART[]             = ".uno:AvailableToolbars?Toolbar:string=";
76 static const char STATIC_INTERNAL_CMD_PART[]    = ".cmd:";
77 
78 namespace framework
79 {
80 
81 typedef std::unordered_map< OUString, OUString > ToolbarHashMap;
82 
83 struct ToolBarEntry
84 {
85     OUString           aUIName;
86     OUString           aCommand;
87     bool                bVisible;
88     bool                bContextSensitive;
89     const CollatorWrapper*  pCollatorWrapper;
90 };
91 
92 static bool CompareToolBarEntry( const ToolBarEntry& aOne, const ToolBarEntry& aTwo )
93 {
94     sal_Int32 nComp = aOne.pCollatorWrapper->compareString( aOne.aUIName, aTwo.aUIName );
95 
96     return nComp < 0;
97 }
98 
99 static Reference< XLayoutManager > getLayoutManagerFromFrame( const Reference< XFrame >& rFrame )
100 {
101     Reference< XPropertySet >   xPropSet( rFrame, UNO_QUERY );
102     Reference< XLayoutManager > xLayoutManager;
103 
104     try
105     {
106         xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
107     }
108     catch ( const UnknownPropertyException& )
109     {
110     }
111 
112     return xLayoutManager;
113 }
114 
115 struct ToolBarInfo
116 {
117     OUString aToolBarResName;
118     OUString aToolBarUIName;
119 };
120 
121 DEFINE_XSERVICEINFO_MULTISERVICE_2      (   ToolbarsMenuController                  ,
122                                             OWeakObject                             ,
123                                             SERVICENAME_POPUPMENUCONTROLLER         ,
124                                             IMPLEMENTATIONNAME_TOOLBARSMENUCONTROLLER
125                                         )
126 
127 DEFINE_INIT_SERVICE                     (   ToolbarsMenuController, {} )
128 
129 static constexpr OUStringLiteral g_aPropUIName( "UIName" );
130 static constexpr OUStringLiteral g_aPropResourceURL( "ResourceURL" );
131 
132 ToolbarsMenuController::ToolbarsMenuController( const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
133     svt::PopupMenuControllerBase( xContext ),
134     m_xContext( xContext ),
135     m_bResetActive( false ),
136     m_aIntlWrapper(SvtSysLocale().GetUILanguageTag())
137 {
138 }
139 
140 ToolbarsMenuController::~ToolbarsMenuController()
141 {
142 }
143 
144 void ToolbarsMenuController::addCommand(
145     Reference< css::awt::XPopupMenu > const & rPopupMenu, const OUString& rCommandURL, const OUString& rLabel )
146 {
147     sal_uInt16        nItemId    = m_xPopupMenu->getItemCount()+1;
148 
149     OUString aLabel;
150     if ( rLabel.isEmpty() )
151         aLabel = vcl::CommandInfoProvider::GetMenuLabelForCommand( rCommandURL, m_aModuleName );
152     else
153         aLabel = rLabel;
154 
155     rPopupMenu->insertItem( nItemId, aLabel, 0, nItemId );
156     rPopupMenu->setCommand( nItemId, rCommandURL );
157 
158     bool bInternal = rCommandURL.startsWith( STATIC_INTERNAL_CMD_PART );
159     if ( !bInternal )
160     {
161         if ( !getDispatchFromCommandURL( rCommandURL ).is() )
162             m_xPopupMenu->enableItem( nItemId, false );
163     }
164 
165     SolarMutexGuard aSolarMutexGuard;
166 
167     Image                aImage;
168     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
169 
170     if ( rSettings.GetUseImagesInMenus() )
171         aImage = vcl::CommandInfoProvider::GetImageForCommand(rCommandURL, m_xFrame);
172 
173     VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(VCLXPopupMenu::getImplementation( rPopupMenu ));
174     if ( pPopupMenu )
175     {
176         PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
177         if ( !!aImage )
178             pVCLPopupMenu->SetItemImage( nItemId, aImage );
179     }
180 
181     m_aCommandVector.push_back( rCommandURL );
182 }
183 
184 Reference< XDispatch > ToolbarsMenuController::getDispatchFromCommandURL( const OUString& rCommandURL )
185 {
186     URL                          aTargetURL;
187     Reference< XURLTransformer > xURLTransformer;
188     Reference< XFrame >          xFrame;
189 
190     {
191         SolarMutexGuard aSolarMutexGuard;
192         xURLTransformer = m_xURLTransformer;
193         xFrame = m_xFrame;
194     }
195 
196     aTargetURL.Complete = rCommandURL;
197     xURLTransformer->parseStrict( aTargetURL );
198     Reference< XDispatchProvider > xDispatchProvider( xFrame, UNO_QUERY );
199     if ( xDispatchProvider.is() )
200         return xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
201     else
202         return Reference< XDispatch >();
203 }
204 
205 static void fillHashMap( const Sequence< Sequence< css::beans::PropertyValue > >& rSeqToolBars,
206                          ToolbarHashMap& rHashMap )
207 {
208     for ( sal_Int32 i = 0; i < rSeqToolBars.getLength(); i++ )
209     {
210         OUString aResourceURL;
211         OUString aUIName;
212         const PropertyValue* pProperties = rSeqToolBars[i].getConstArray();
213         for ( sal_Int32 j = 0; j < rSeqToolBars[i].getLength(); j++ )
214         {
215             if ( pProperties[j].Name == "ResourceURL" )
216                 pProperties[j].Value >>= aResourceURL;
217             else if ( pProperties[j].Name == "UIName" )
218                 pProperties[j].Value >>= aUIName;
219         }
220 
221         if ( !aResourceURL.isEmpty() &&
222              rHashMap.find( aResourceURL ) == rHashMap.end() )
223             rHashMap.emplace( aResourceURL, aUIName );
224     }
225 }
226 
227 // private function
228 Sequence< Sequence< css::beans::PropertyValue > > ToolbarsMenuController::getLayoutManagerToolbars( const Reference< css::frame::XLayoutManager >& rLayoutManager )
229 {
230     std::vector< ToolBarInfo > aToolBarArray;
231     Sequence< Reference< XUIElement > > aUIElements = rLayoutManager->getElements();
232     for ( sal_Int32 i = 0; i < aUIElements.getLength(); i++ )
233     {
234         Reference< XUIElement > xUIElement( aUIElements[i] );
235         Reference< XPropertySet > xPropSet( aUIElements[i], UNO_QUERY );
236         if ( xPropSet.is() && xUIElement.is() )
237         {
238             try
239             {
240                 OUString   aResName;
241                 sal_Int16       nType( -1 );
242                 xPropSet->getPropertyValue("Type") >>= nType;
243                 xPropSet->getPropertyValue("ResourceURL") >>= aResName;
244 
245                 if (( nType == css::ui::UIElementType::TOOLBAR ) &&
246                     !aResName.isEmpty() )
247                 {
248                     ToolBarInfo aToolBarInfo;
249 
250                     aToolBarInfo.aToolBarResName = aResName;
251 
252                     SolarMutexGuard aGuard;
253                     Reference< css::awt::XWindow > xWindow( xUIElement->getRealInterface(), UNO_QUERY );
254                     VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
255                     if ( pWindow )
256                         aToolBarInfo.aToolBarUIName = pWindow->GetText();
257 
258                     aToolBarArray.push_back( aToolBarInfo );
259                 }
260             }
261             catch ( const Exception& )
262             {
263             }
264         }
265     }
266 
267     Sequence< css::beans::PropertyValue > aTbSeq( 2 );
268     aTbSeq[0].Name = g_aPropUIName;
269     aTbSeq[1].Name = g_aPropResourceURL;
270 
271     Sequence< Sequence< css::beans::PropertyValue > > aSeq( aToolBarArray.size() );
272     const sal_uInt32 nCount = aToolBarArray.size();
273     for ( sal_uInt32 i = 0; i < nCount; i++ )
274     {
275         aTbSeq[0].Value <<= aToolBarArray[i].aToolBarUIName;
276         aTbSeq[1].Value <<= aToolBarArray[i].aToolBarResName;
277         aSeq[i] = aTbSeq;
278     }
279 
280     return aSeq;
281 }
282 
283 
284 void ToolbarsMenuController::fillPopupMenu( Reference< css::awt::XPopupMenu > const & rPopupMenu )
285 {
286     if( SvtMiscOptions().DisableUICustomization() )
287         return;
288 
289     SolarMutexGuard aSolarMutexGuard;
290     resetPopupMenu( rPopupMenu );
291 
292     m_aCommandVector.clear();
293 
294     // Retrieve layout manager for additional information
295     Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( m_xFrame ));
296 
297     m_bResetActive = false;
298     if ( xLayoutManager.is() )
299     {
300         ToolbarHashMap aToolbarHashMap;
301 
302         if ( m_xDocCfgMgr.is() )
303         {
304             Sequence< Sequence< css::beans::PropertyValue > > aSeqDocToolBars =
305                 m_xDocCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR );
306             fillHashMap( aSeqDocToolBars, aToolbarHashMap );
307         }
308 
309         if ( m_xModuleCfgMgr.is() )
310         {
311             Sequence< Sequence< css::beans::PropertyValue > > aSeqToolBars =
312                 m_xModuleCfgMgr->getUIElementsInfo( UIElementType::TOOLBAR );
313             fillHashMap( aSeqToolBars, aToolbarHashMap );
314         }
315 
316         std::vector< ToolBarEntry > aSortedTbs;
317         OUString               aStaticCmdPart( STATIC_CMD_PART );
318 
319         Sequence< Sequence< css::beans::PropertyValue > > aSeqFrameToolBars = getLayoutManagerToolbars( xLayoutManager );
320         fillHashMap( aSeqFrameToolBars, aToolbarHashMap );
321 
322         for (auto const& toolbar : aToolbarHashMap)
323         {
324             OUString aUIName = toolbar.second;
325             bool      bHideFromMenu( false );
326             bool      bContextSensitive( false );
327             if ( aUIName.isEmpty() &&
328                  m_xPersistentWindowState.is() )
329             {
330                 bool bVisible( false );
331 
332                 try
333                 {
334                     Sequence< PropertyValue > aWindowState;
335                     Any                       a( m_xPersistentWindowState->getByName( toolbar.first ));
336 
337                     if ( a >>= aWindowState )
338                     {
339                         for ( sal_Int32 i = 0; i < aWindowState.getLength(); i++ )
340                         {
341                             if ( aWindowState[i].Name == WINDOWSTATE_PROPERTY_UINAME )
342                                 aWindowState[i].Value >>= aUIName;
343                             else if ( aWindowState[i].Name == WINDOWSTATE_PROPERTY_HIDEFROMENU )
344                                 aWindowState[i].Value >>= bHideFromMenu;
345                             else if ( aWindowState[i].Name == WINDOWSTATE_PROPERTY_CONTEXT )
346                                 aWindowState[i].Value >>= bContextSensitive;
347                             else if ( aWindowState[i].Name == WINDOWSTATE_PROPERTY_VISIBLE )
348                                 aWindowState[i].Value >>= bVisible;
349                         }
350                     }
351                 }
352                 catch ( const Exception& )
353                 {
354                 }
355 
356                 // Check if we have to enable/disable "Reset" menu item
357                 if ( bContextSensitive && !bVisible )
358                     m_bResetActive = true;
359 
360             }
361 
362             if ( !aUIName.isEmpty() && !bHideFromMenu )
363             {
364                 ToolBarEntry aTbEntry;
365                 aTbEntry.aUIName = aUIName;
366                 aTbEntry.aCommand = toolbar.first;
367                 aTbEntry.bVisible = xLayoutManager->isElementVisible( toolbar.first );
368                 aTbEntry.bContextSensitive = bContextSensitive;
369                 aTbEntry.pCollatorWrapper = m_aIntlWrapper.getCaseCollator();
370                 aSortedTbs.push_back( aTbEntry );
371             }
372         }
373 
374         // sort toolbars
375         std::sort( aSortedTbs.begin(), aSortedTbs.end(), CompareToolBarEntry );
376 
377         sal_Int16 nIndex( 1 );
378         const sal_uInt32 nCount = aSortedTbs.size();
379         for ( sal_uInt32 i = 0; i < nCount; i++ )
380         {
381             sal_uInt16 nItemCount = m_xPopupMenu->getItemCount();
382             m_xPopupMenu->insertItem( nIndex, aSortedTbs[i].aUIName, css::awt::MenuItemStyle::CHECKABLE, nItemCount );
383             if ( aSortedTbs[i].bVisible )
384                 m_xPopupMenu->checkItem( nIndex, true );
385 
386             {
387                 SolarMutexGuard aGuard;
388                 VCLXPopupMenu* pXPopupMenu = static_cast<VCLXPopupMenu *>(VCLXMenu::getImplementation( m_xPopupMenu ));
389                 PopupMenu* pVCLPopupMenu = pXPopupMenu ? static_cast<PopupMenu *>(pXPopupMenu->GetMenu()) : nullptr;
390                 assert(pVCLPopupMenu);
391                 if (pVCLPopupMenu)
392                     pVCLPopupMenu->SetUserValue( nIndex, reinterpret_cast<void*>( aSortedTbs[i].bContextSensitive ? 1 : 0 ));
393             }
394 
395             // use VCL popup menu pointer to set vital information that are not part of the awt implementation
396             OUStringBuffer aStrBuf( aStaticCmdPart );
397 
398             sal_Int32 n = aSortedTbs[i].aCommand.lastIndexOf( '/' );
399             if (( n > 0 ) && (( n+1 ) < aSortedTbs[i].aCommand.getLength() ))
400                 aStrBuf.append( std::u16string_view(aSortedTbs[i].aCommand).substr(n+1) );
401 
402             OUString aCmd( aStrBuf.makeStringAndClear() );
403 
404             // Store complete uno-command so it can also be dispatched. This is necessary to support
405             // the test tool!
406             rPopupMenu->setCommand( nIndex, aCmd );
407             ++nIndex;
408         }
409 
410         // Create commands for non-toolbars
411         if ( m_aModuleIdentifier == "com.sun.star.text.TextDocument" ||
412              m_aModuleIdentifier == "com.sun.star.text.WebDocument" ||
413              m_aModuleIdentifier == "com.sun.star.text.GlobalDocument" ||
414              m_aModuleIdentifier == "com.sun.star.drawing.DrawingDocument" ||
415              m_aModuleIdentifier == "com.sun.star.presentation.PresentationDocument" ||
416              m_aModuleIdentifier == "com.sun.star.sheet.SpreadsheetDocument" )
417         {
418             if ( m_aModuleIdentifier == "com.sun.star.drawing.DrawingDocument" ||
419                  m_aModuleIdentifier == "com.sun.star.presentation.PresentationDocument" )
420                 addCommand( m_xPopupMenu, ".uno:ColorControl", "" );
421             else if ( m_aModuleIdentifier == "com.sun.star.sheet.SpreadsheetDocument" )
422                 addCommand( m_xPopupMenu, ".uno:InputLineVisible", "" );
423             else
424                 addCommand( m_xPopupMenu, ".uno:InsertFormula", "" );
425         }
426 
427         bool          bAddCommand( true );
428         SvtCommandOptions aCmdOptions;
429 
430         if ( aCmdOptions.HasEntries( SvtCommandOptions::CMDOPTION_DISABLED ))
431         {
432             if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED,
433                                      "ConfigureDialog"))
434                 bAddCommand = false;
435         }
436 
437         if ( bAddCommand )
438         {
439             // Create command for configure
440             if ( m_xPopupMenu->getItemCount() > 0 )
441             {
442                 sal_uInt16        nItemCount = m_xPopupMenu->getItemCount();
443                 m_xPopupMenu->insertSeparator( nItemCount+1 );
444             }
445 
446             addCommand( m_xPopupMenu, ".uno:ConfigureDialog", "" );
447         }
448 
449         // Add separator if no configure has been added
450         if ( !bAddCommand )
451         {
452             // Create command for configure
453             if ( m_xPopupMenu->getItemCount() > 0 )
454             {
455                 sal_uInt16        nItemCount = m_xPopupMenu->getItemCount();
456                 m_xPopupMenu->insertSeparator( nItemCount+1 );
457             }
458         }
459 
460         OUString aLabelStr(FwkResId(STR_RESTORE_TOOLBARS));
461         OUString aRestoreCmd( CMD_RESTOREVISIBILITY );
462         addCommand( m_xPopupMenu, aRestoreCmd, aLabelStr );
463     }
464 }
465 
466 // XEventListener
467 void SAL_CALL ToolbarsMenuController::disposing( const EventObject& )
468 {
469     Reference< css::awt::XMenuListener > xHolder(static_cast<OWeakObject *>(this), UNO_QUERY );
470 
471     osl::MutexGuard aLock( m_aMutex );
472     m_xFrame.clear();
473     m_xDispatch.clear();
474     m_xDocCfgMgr.clear();
475     m_xModuleCfgMgr.clear();
476     m_xContext.clear();
477 
478     if ( m_xPopupMenu.is() )
479         m_xPopupMenu->removeMenuListener( Reference< css::awt::XMenuListener >(static_cast<OWeakObject *>(this), UNO_QUERY ));
480     m_xPopupMenu.clear();
481 }
482 
483 // XStatusListener
484 void SAL_CALL ToolbarsMenuController::statusChanged( const FeatureStateEvent& Event )
485 {
486     OUString aFeatureURL( Event.FeatureURL.Complete );
487 
488     // All other status events will be processed here
489     osl::ClearableMutexGuard aLock( m_aMutex );
490     Reference< css::awt::XPopupMenu > xPopupMenu( m_xPopupMenu );
491     aLock.clear();
492 
493     if ( xPopupMenu.is() )
494     {
495         SolarMutexGuard aGuard;
496         VCLXPopupMenu* pXPopupMenu = static_cast<VCLXPopupMenu *>(VCLXMenu::getImplementation( xPopupMenu ));
497         PopupMenu*     pVCLPopupMenu = pXPopupMenu ? static_cast<PopupMenu *>(pXPopupMenu->GetMenu()) : nullptr;
498 
499         SAL_WARN_IF(!pVCLPopupMenu, "fwk.uielement", "worrying lack of popup menu");
500         if (!pVCLPopupMenu)
501             return;
502 
503         bool bSetCheckmark      = false;
504         bool bCheckmark         = false;
505         for ( sal_uInt16 i = 0; i < pVCLPopupMenu->GetItemCount(); i++ )
506         {
507             sal_uInt16 nId = pVCLPopupMenu->GetItemId( i );
508             if ( nId == 0 )
509                 continue;
510 
511             OUString aCmd = pVCLPopupMenu->GetItemCommand( nId );
512             if ( aCmd == aFeatureURL )
513             {
514                 // Enable/disable item
515                 pVCLPopupMenu->EnableItem( nId, Event.IsEnabled );
516 
517                 // Checkmark
518                 if ( Event.State >>= bCheckmark )
519                     bSetCheckmark = true;
520 
521                 if ( bSetCheckmark )
522                     pVCLPopupMenu->CheckItem( nId, bCheckmark );
523                 else
524                 {
525                     OUString aItemText;
526 
527                     if ( Event.State >>= aItemText )
528                         pVCLPopupMenu->SetItemText( nId, aItemText );
529                 }
530             }
531         }
532     }
533 }
534 
535 // XMenuListener
536 void SAL_CALL ToolbarsMenuController::itemSelected( const css::awt::MenuEvent& rEvent )
537 {
538     Reference< css::awt::XPopupMenu >   xPopupMenu;
539     Reference< XComponentContext >      xContext;
540     Reference< XURLTransformer >        xURLTransformer;
541     Reference< XFrame >                 xFrame;
542     Reference< XNameAccess >            xPersistentWindowState;
543 
544     {
545         osl::MutexGuard aLock(m_aMutex);
546         xPopupMenu = m_xPopupMenu;
547         xContext = m_xContext;
548         xURLTransformer = m_xURLTransformer;
549         xFrame = m_xFrame;
550         xPersistentWindowState = m_xPersistentWindowState;
551     }
552 
553     if ( xPopupMenu.is() )
554     {
555         VCLXPopupMenu* pPopupMenu = static_cast<VCLXPopupMenu *>(VCLXPopupMenu::getImplementation( xPopupMenu ));
556         if ( pPopupMenu )
557         {
558             SolarMutexGuard aSolarMutexGuard;
559             PopupMenu* pVCLPopupMenu = static_cast<PopupMenu *>(pPopupMenu->GetMenu());
560 
561             OUString aCmd( pVCLPopupMenu->GetItemCommand( rEvent.MenuId ));
562             if ( aCmd.startsWith( STATIC_INTERNAL_CMD_PART ) )
563             {
564                 // Command to restore the visibility of all context sensitive toolbars
565                 Reference< XNameReplace > xNameReplace( xPersistentWindowState, UNO_QUERY );
566                 if ( xPersistentWindowState.is() && xNameReplace.is() )
567                 {
568                     try
569                     {
570                         Sequence< OUString > aElementNames = xPersistentWindowState->getElementNames();
571                         sal_Int32 nCount = aElementNames.getLength();
572                         bool      bRefreshToolbars( false );
573 
574                         for ( sal_Int32 i = 0; i < nCount; i++ )
575                         {
576                             try
577                             {
578                                 OUString aElementName = aElementNames[i];
579                                 Sequence< PropertyValue > aWindowState;
580 
581                                 if ( xPersistentWindowState->getByName( aElementName ) >>= aWindowState )
582                                 {
583                                     bool  bVisible( false );
584                                     bool  bContextSensitive( false );
585                                     sal_Int32 nVisibleIndex( -1 );
586                                     for ( sal_Int32 j = 0; j < aWindowState.getLength(); j++ )
587                                     {
588                                         if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_VISIBLE )
589                                         {
590                                             aWindowState[j].Value >>= bVisible;
591                                             nVisibleIndex = j;
592                                         }
593                                         else if ( aWindowState[j].Name == WINDOWSTATE_PROPERTY_CONTEXT )
594                                             aWindowState[j].Value >>= bContextSensitive;
595                                     }
596 
597                                     if ( !bVisible && bContextSensitive && nVisibleIndex >= 0 )
598                                     {
599                                         // Default is: Every context sensitive toolbar is visible
600                                         aWindowState[nVisibleIndex].Value <<= true;
601                                         xNameReplace->replaceByName( aElementName, makeAny( aWindowState ));
602                                         bRefreshToolbars = true;
603                                     }
604                                 }
605                             }
606                             catch ( const NoSuchElementException& )
607                             {
608                             }
609                         }
610 
611                         if ( bRefreshToolbars )
612                         {
613                             Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame ));
614                             if ( xLayoutManager.is() )
615                             {
616                                 Reference< XPropertySet > xPropSet( xLayoutManager, UNO_QUERY );
617                                 if ( xPropSet.is() )
618                                 {
619                                     try
620                                     {
621                                         xPropSet->setPropertyValue("RefreshContextToolbarVisibility", makeAny( true ));
622                                     }
623                                     catch ( const RuntimeException& )
624                                     {
625                                     }
626                                     catch ( const Exception& )
627                                     {
628                                     }
629                                 }
630                             }
631                             RefreshToolbars( xFrame );
632                         }
633                     }
634                     catch ( const RuntimeException& )
635                     {
636                         throw;
637                     }
638                     catch ( const Exception& )
639                     {
640                     }
641                 }
642             }
643             else if ( aCmd.indexOf( STATIC_CMD_PART ) < 0 )
644             {
645                 URL                     aTargetURL;
646                 Sequence<PropertyValue> aArgs;
647 
648                 aTargetURL.Complete = aCmd;
649                 xURLTransformer->parseStrict( aTargetURL );
650                 Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
651                 if ( xDispatchProvider.is() )
652                 {
653                     Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch(
654                                                             aTargetURL, OUString(), 0 );
655 
656                     ExecuteInfo* pExecuteInfo = new ExecuteInfo;
657                     pExecuteInfo->xDispatch     = xDispatch;
658                     pExecuteInfo->aTargetURL    = aTargetURL;
659                     pExecuteInfo->aArgs         = aArgs;
660                     Application::PostUserEvent( LINK(nullptr, ToolbarsMenuController, ExecuteHdl_Impl), pExecuteInfo );
661                 }
662             }
663             else
664             {
665                 Reference< XLayoutManager > xLayoutManager( getLayoutManagerFromFrame( xFrame ));
666                 if ( xLayoutManager.is() )
667                 {
668                     // Extract toolbar name from the combined uno-command.
669                     sal_Int32 nIndex = aCmd.indexOf( '=' );
670                     if (( nIndex > 0 ) && (( nIndex+1 ) < aCmd.getLength() ))
671                     {
672                         OUStringBuffer aBuf( "private:resource/toolbar/" );
673                         aBuf.append( std::u16string_view(aCmd).substr(nIndex+1) );
674 
675                         bool      bShow( !pVCLPopupMenu->IsItemChecked( rEvent.MenuId ));
676                         OUString aToolBarResName( aBuf.makeStringAndClear() );
677                         if ( bShow )
678                         {
679                             xLayoutManager->createElement( aToolBarResName );
680                             xLayoutManager->showElement( aToolBarResName );
681                         }
682                         else
683                         {
684                             // closing means:
685                             // hide and destroy element
686                             xLayoutManager->hideElement( aToolBarResName );
687                             xLayoutManager->destroyElement( aToolBarResName );
688                         }
689                     }
690                 }
691             }
692         }
693     }
694 }
695 
696 void SAL_CALL ToolbarsMenuController::itemActivated( const css::awt::MenuEvent& )
697 {
698     std::vector< OUString >   aCmdVector;
699     Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
700     Reference< XURLTransformer >   xURLTransformer( m_xURLTransformer );
701     {
702         osl::MutexGuard aLock( m_aMutex );
703         fillPopupMenu( m_xPopupMenu );
704         aCmdVector = m_aCommandVector;
705     }
706 
707     // Update status for all commands inside our toolbars popup menu
708     const sal_uInt32 nCount = aCmdVector.size();
709     for ( sal_uInt32 i = 0; i < nCount; i++ )
710     {
711         bool bInternal = aCmdVector[i].startsWith( STATIC_INTERNAL_CMD_PART );
712 
713         if ( !bInternal )
714         {
715             URL aTargetURL;
716             aTargetURL.Complete = aCmdVector[i];
717             xURLTransformer->parseStrict( aTargetURL );
718             Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
719             if ( xDispatch.is() )
720             {
721                 xDispatch->addStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
722                 xDispatch->removeStatusListener( static_cast< XStatusListener* >(this), aTargetURL );
723             }
724         }
725         else if ( aCmdVector[i] == CMD_RESTOREVISIBILITY )
726         {
727             // Special code to determine the enable/disable state of this command
728             FeatureStateEvent aFeatureStateEvent;
729             aFeatureStateEvent.FeatureURL.Complete = aCmdVector[i];
730             aFeatureStateEvent.IsEnabled = m_bResetActive; // is context sensitive toolbar non visible
731             statusChanged( aFeatureStateEvent );
732         }
733     }
734 }
735 
736 // XPopupMenuController
737 void SAL_CALL ToolbarsMenuController::setPopupMenu( const Reference< css::awt::XPopupMenu >& xPopupMenu )
738 {
739     osl::MutexGuard aLock( m_aMutex );
740 
741     throwIfDisposed();
742 
743     if ( m_xFrame.is() && !m_xPopupMenu.is() )
744     {
745         // Create popup menu on demand
746         SolarMutexGuard aSolarMutexGuard;
747 
748         m_xPopupMenu = xPopupMenu;
749         m_xPopupMenu->addMenuListener( Reference< css::awt::XMenuListener >( static_cast<OWeakObject*>(this), UNO_QUERY ));
750         fillPopupMenu( m_xPopupMenu );
751     }
752 }
753 
754 // XInitialization
755 void SAL_CALL ToolbarsMenuController::initialize( const Sequence< Any >& aArguments )
756 {
757     osl::MutexGuard aLock( m_aMutex );
758     bool bInitalized( m_bInitialized );
759     if ( !bInitalized )
760     {
761         svt::PopupMenuControllerBase::initialize(aArguments);
762 
763         if ( m_bInitialized )
764         {
765             Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
766             Reference< XNameAccess > xPersistentWindowStateSupplier = css::ui::theWindowStateConfiguration::get( m_xContext );
767 
768             // Retrieve persistent window state reference for our module
769             OUString aModuleIdentifier;
770             try
771             {
772                 aModuleIdentifier = xModuleManager->identify( m_xFrame );
773                 xPersistentWindowStateSupplier->getByName( aModuleIdentifier ) >>= m_xPersistentWindowState;
774 
775                 Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgSupplier =
776                     theModuleUIConfigurationManagerSupplier::get( m_xContext );
777                 m_xModuleCfgMgr = xModuleCfgSupplier->getUIConfigurationManager( aModuleIdentifier );
778 
779                 Reference< XController > xController = m_xFrame->getController();
780                 Reference< XModel >      xModel;
781                 if ( xController.is() )
782                     xModel = xController->getModel();
783                 if ( xModel.is() )
784                 {
785                     Reference< XUIConfigurationManagerSupplier > xUIConfigurationManagerSupplier( xModel, UNO_QUERY );
786                     if ( xUIConfigurationManagerSupplier.is() )
787                         m_xDocCfgMgr = xUIConfigurationManagerSupplier->getUIConfigurationManager();
788                 }
789                 m_aModuleIdentifier = aModuleIdentifier;
790             }
791             catch ( const Exception& )
792             {
793             }
794         }
795     }
796 }
797 
798 IMPL_STATIC_LINK( ToolbarsMenuController, ExecuteHdl_Impl, void*, p, void )
799 {
800     ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
801     try
802     {
803         // Asynchronous execution as this can lead to our own destruction!
804         // Framework can recycle our current frame and the layout manager disposes all user interface
805         // elements if a component gets detached from its frame!
806         if ( pExecuteInfo->xDispatch.is() )
807         {
808             pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
809         }
810     }
811     catch ( const Exception& )
812     {
813     }
814 
815     delete pExecuteInfo;
816 }
817 
818 }
819 
820 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
821