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 <bitmaps.hlst>
21 
22 #include <cppuhelper/implbase.hxx>
23 #include <cppuhelper/supportsservice.hxx>
24 #include <comphelper/propertyvalue.hxx>
25 #include <menuconfiguration.hxx>
26 #include <svtools/imagemgr.hxx>
27 #include <svtools/toolboxcontroller.hxx>
28 #include <toolkit/awt/vclxmenu.hxx>
29 #include <toolkit/helper/vclunohelper.hxx>
30 #include <tools/diagnose_ex.h>
31 #include <tools/urlobj.hxx>
32 #include <vcl/commandinfoprovider.hxx>
33 #include <vcl/menu.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/toolbox.hxx>
36 
37 #include <com/sun/star/awt/PopupMenuDirection.hpp>
38 #include <com/sun/star/awt/XPopupMenu.hpp>
39 #include <com/sun/star/frame/thePopupMenuControllerFactory.hpp>
40 #include <com/sun/star/frame/XPopupMenuController.hpp>
41 #include <com/sun/star/frame/XStorable.hpp>
42 #include <com/sun/star/frame/XSubToolbarController.hpp>
43 #include <com/sun/star/frame/XUIControllerFactory.hpp>
44 #include <com/sun/star/frame/XController.hpp>
45 #include <com/sun/star/lang/XServiceInfo.hpp>
46 #include <com/sun/star/ucb/CommandFailedException.hpp>
47 #include <com/sun/star/ucb/ContentCreationException.hpp>
48 #include <com/sun/star/util/XModifiable.hpp>
49 
50 using namespace framework;
51 
52 namespace
53 {
54 
55 typedef cppu::ImplInheritanceHelper< svt::ToolboxController,
56                                     css::lang::XServiceInfo >
57                 ToolBarBase;
58 
59 class PopupMenuToolbarController : public ToolBarBase
60 {
61 public:
62     // XComponent
63     virtual void SAL_CALL dispose() override;
64     // XInitialization
65     virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
66     // XToolbarController
67     virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override;
68     // XStatusListener
69     virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
70 
71 protected:
72     PopupMenuToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
73                                 const OUString &rPopupCommand = OUString() );
74     virtual void functionExecuted( const OUString &rCommand );
75     virtual ToolBoxItemBits getDropDownStyle() const;
76     void createPopupMenuController();
77 
78     bool                                                    m_bHasController;
79     bool                                                    m_bResourceURL;
80     OUString                                                m_aPopupCommand;
81     css::uno::Reference< css::awt::XPopupMenu >             m_xPopupMenu;
82 
83 private:
84     css::uno::Reference< css::frame::XUIControllerFactory > m_xPopupMenuFactory;
85     css::uno::Reference< css::frame::XPopupMenuController > m_xPopupMenuController;
86 };
87 
PopupMenuToolbarController(const css::uno::Reference<css::uno::XComponentContext> & xContext,const OUString & rPopupCommand)88 PopupMenuToolbarController::PopupMenuToolbarController(
89     const css::uno::Reference< css::uno::XComponentContext >& xContext,
90     const OUString &rPopupCommand )
91     : ToolBarBase( xContext, css::uno::Reference< css::frame::XFrame >(), /*aCommandURL*/OUString() )
92     , m_bHasController( false )
93     , m_bResourceURL( false )
94     , m_aPopupCommand( rPopupCommand )
95 {
96 }
97 
dispose()98 void SAL_CALL PopupMenuToolbarController::dispose()
99 {
100     svt::ToolboxController::dispose();
101 
102     osl::MutexGuard aGuard( m_aMutex );
103     if( m_xPopupMenuController.is() )
104     {
105         css::uno::Reference< css::lang::XComponent > xComponent(
106             m_xPopupMenuController, css::uno::UNO_QUERY );
107         if( xComponent.is() )
108         {
109             try
110             {
111                 xComponent->dispose();
112             }
113             catch (...)
114             {}
115         }
116         m_xPopupMenuController.clear();
117     }
118 
119     m_xContext.clear();
120     m_xPopupMenuFactory.clear();
121     m_xPopupMenu.clear();
122 }
123 
initialize(const css::uno::Sequence<css::uno::Any> & aArguments)124 void SAL_CALL PopupMenuToolbarController::initialize(
125     const css::uno::Sequence< css::uno::Any >& aArguments )
126 {
127     ToolboxController::initialize( aArguments );
128 
129     osl::MutexGuard aGuard( m_aMutex );
130     if ( !m_aPopupCommand.getLength() )
131         m_aPopupCommand = m_aCommandURL;
132 
133     try
134     {
135         m_xPopupMenuFactory.set(
136             css::frame::thePopupMenuControllerFactory::get( m_xContext ) );
137         m_bHasController = m_xPopupMenuFactory->hasController(
138             m_aPopupCommand, getModuleName() );
139     }
140     catch (const css::uno::Exception&)
141     {
142         TOOLS_INFO_EXCEPTION( "fwk.uielement", "" );
143     }
144 
145     if ( !m_bHasController && m_aPopupCommand.startsWith( "private:resource/" ) )
146     {
147         m_bResourceURL = true;
148         m_bHasController = true;
149     }
150 
151     SolarMutexGuard aSolarLock;
152     ToolBox* pToolBox = nullptr;
153     ToolBoxItemId nItemId;
154     if ( getToolboxId( nItemId, &pToolBox ) )
155     {
156         ToolBoxItemBits nCurStyle( pToolBox->GetItemBits( nItemId ) );
157         ToolBoxItemBits nSetStyle( getDropDownStyle() );
158         pToolBox->SetItemBits( nItemId,
159                                m_bHasController ?
160                                     nCurStyle | nSetStyle :
161                                     nCurStyle & ~nSetStyle );
162     }
163 
164 }
165 
statusChanged(const css::frame::FeatureStateEvent & rEvent)166 void SAL_CALL PopupMenuToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
167 {
168     if ( m_bResourceURL )
169         return;
170 
171     ToolBox* pToolBox = nullptr;
172     ToolBoxItemId nItemId;
173     if ( getToolboxId( nItemId, &pToolBox ) )
174     {
175         SolarMutexGuard aSolarLock;
176         pToolBox->EnableItem( nItemId, rEvent.IsEnabled );
177         bool bValue;
178         if ( rEvent.State >>= bValue )
179             pToolBox->CheckItem( nItemId, bValue );
180     }
181 }
182 
183 css::uno::Reference< css::awt::XWindow > SAL_CALL
createPopupWindow()184 PopupMenuToolbarController::createPopupWindow()
185 {
186     css::uno::Reference< css::awt::XWindow > xRet;
187 
188     osl::MutexGuard aGuard( m_aMutex );
189     if ( !m_bHasController )
190         return xRet;
191 
192     createPopupMenuController();
193 
194     SolarMutexGuard aSolarLock;
195     VclPtr< ToolBox > pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
196     if ( !pToolBox )
197         return xRet;
198 
199     pToolBox->SetItemDown( m_nToolBoxId, true );
200     WindowAlign eAlign( pToolBox->GetAlign() );
201 
202     // If the parent ToolBox is in popup mode (e.g. sub toolbar, overflow popup),
203     // its ToolBarManager can be disposed along with our controller, destroying
204     // m_xPopupMenu, while the latter still in execute. This should be fixed at a
205     // different level, for now just hold it here so it won't crash.
206     css::uno::Reference< css::awt::XPopupMenu > xPopupMenu ( m_xPopupMenu );
207     sal_uInt16 nId = xPopupMenu->execute(
208         css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ),
209         VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ),
210         ( eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom ) ?
211             css::awt::PopupMenuDirection::EXECUTE_DOWN :
212             css::awt::PopupMenuDirection::EXECUTE_RIGHT );
213     pToolBox->SetItemDown( m_nToolBoxId, false );
214 
215     if ( nId )
216         functionExecuted( xPopupMenu->getCommand( nId ) );
217 
218     return xRet;
219 }
220 
functionExecuted(const OUString &)221 void PopupMenuToolbarController::functionExecuted( const OUString &/*rCommand*/)
222 {
223 }
224 
getDropDownStyle() const225 ToolBoxItemBits PopupMenuToolbarController::getDropDownStyle() const
226 {
227     return ToolBoxItemBits::DROPDOWN;
228 }
229 
createPopupMenuController()230 void PopupMenuToolbarController::createPopupMenuController()
231 {
232     if( !m_bHasController )
233         return;
234 
235     if ( m_xPopupMenuController.is() )
236     {
237         m_xPopupMenuController->updatePopupMenu();
238     }
239     else
240     {
241         css::uno::Sequence<css::uno::Any> aArgs {
242             css::uno::makeAny(comphelper::makePropertyValue("Frame", m_xFrame)),
243             css::uno::makeAny(comphelper::makePropertyValue("ModuleIdentifier", m_sModuleName)),
244             css::uno::makeAny(comphelper::makePropertyValue("InToolbar", true))
245         };
246 
247         try
248         {
249             m_xPopupMenu.set(
250                 m_xContext->getServiceManager()->createInstanceWithContext(
251                     "com.sun.star.awt.PopupMenu", m_xContext ),
252                         css::uno::UNO_QUERY_THROW );
253 
254             if (m_bResourceURL)
255             {
256                 sal_Int32 nAppendIndex = aArgs.getLength();
257                 aArgs.realloc(nAppendIndex + 1);
258                 aArgs[nAppendIndex] <<= comphelper::makePropertyValue("ResourceURL", m_aPopupCommand);
259 
260                 m_xPopupMenuController.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
261                     "com.sun.star.comp.framework.ResourceMenuController", aArgs, m_xContext), css::uno::UNO_QUERY_THROW );
262             }
263             else
264             {
265                 m_xPopupMenuController.set( m_xPopupMenuFactory->createInstanceWithArgumentsAndContext(
266                     m_aPopupCommand, aArgs, m_xContext), css::uno::UNO_QUERY_THROW );
267             }
268 
269             m_xPopupMenuController->setPopupMenu( m_xPopupMenu );
270         }
271         catch ( const css::uno::Exception & )
272         {
273             TOOLS_INFO_EXCEPTION( "fwk.uielement", "" );
274             m_xPopupMenu.clear();
275         }
276     }
277 }
278 
279 class GenericPopupToolbarController : public PopupMenuToolbarController
280 {
281 public:
282     GenericPopupToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
283                                    const css::uno::Sequence< css::uno::Any >& rxArgs );
284 
285     // XInitialization
286     virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rxArgs ) override;
287 
288     // XStatusListener
289     virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
290 
291     // XServiceInfo
292     virtual OUString SAL_CALL getImplementationName() override;
293 
294     virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override;
295 
296     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
297 
298 private:
299     bool m_bSplitButton, m_bReplaceWithLast;
300     void functionExecuted(const OUString &rCommand) override;
301     ToolBoxItemBits getDropDownStyle() const override;
302 };
303 
GenericPopupToolbarController(const css::uno::Reference<css::uno::XComponentContext> & xContext,const css::uno::Sequence<css::uno::Any> & rxArgs)304 GenericPopupToolbarController::GenericPopupToolbarController(
305     const css::uno::Reference< css::uno::XComponentContext >& xContext,
306     const css::uno::Sequence< css::uno::Any >& rxArgs )
307     : PopupMenuToolbarController( xContext )
308     , m_bReplaceWithLast( false )
309 {
310     css::beans::PropertyValue aPropValue;
311     for ( const auto& arg: rxArgs )
312     {
313         if ( ( arg >>= aPropValue ) && aPropValue.Name == "Value" )
314         {
315             sal_Int32 nIdx{ 0 };
316             OUString aValue;
317             aPropValue.Value >>= aValue;
318             m_aPopupCommand = aValue.getToken(0, ';', nIdx);
319             m_bReplaceWithLast = aValue.getToken(0, ';', nIdx).toBoolean();
320             break;
321         }
322     }
323     m_bSplitButton = m_bReplaceWithLast || !m_aPopupCommand.isEmpty();
324 }
325 
getImplementationName()326 OUString GenericPopupToolbarController::getImplementationName()
327 {
328     return "com.sun.star.comp.framework.GenericPopupToolbarController";
329 }
330 
supportsService(OUString const & rServiceName)331 sal_Bool GenericPopupToolbarController::supportsService(OUString const & rServiceName)
332 {
333     return cppu::supportsService( this, rServiceName );
334 }
335 
getSupportedServiceNames()336 css::uno::Sequence<OUString> GenericPopupToolbarController::getSupportedServiceNames()
337 {
338     return {"com.sun.star.frame.ToolbarController"};
339 }
340 
initialize(const css::uno::Sequence<css::uno::Any> & rxArgs)341 void GenericPopupToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& rxArgs )
342 {
343     PopupMenuToolbarController::initialize( rxArgs );
344     if ( m_bReplaceWithLast )
345         // Create early, so we can use the menu is statusChanged method.
346         createPopupMenuController();
347 }
348 
statusChanged(const css::frame::FeatureStateEvent & rEvent)349 void GenericPopupToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
350 {
351     SolarMutexGuard aGuard;
352 
353     if ( m_bReplaceWithLast && !rEvent.IsEnabled && m_xPopupMenu.is() )
354     {
355         Menu* pVclMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu )->GetMenu();
356 
357         ToolBox* pToolBox = nullptr;
358         ToolBoxItemId nId;
359         if ( getToolboxId( nId, &pToolBox ) && pToolBox->IsItemEnabled( nId ) )
360         {
361             pVclMenu->Activate();
362             pVclMenu->Deactivate();
363         }
364 
365         for ( sal_uInt16 i = 0; i < pVclMenu->GetItemCount(); ++i )
366         {
367             sal_uInt16 nItemId = pVclMenu->GetItemId( i );
368             if ( nItemId && pVclMenu->IsItemEnabled( nItemId ) && !pVclMenu->GetPopupMenu( nItemId ) )
369             {
370                 functionExecuted( pVclMenu->GetItemCommand( nItemId ) );
371                 return;
372             }
373         }
374     }
375 
376     PopupMenuToolbarController::statusChanged( rEvent );
377 }
378 
functionExecuted(const OUString & rCommand)379 void GenericPopupToolbarController::functionExecuted( const OUString& rCommand )
380 {
381     if ( !m_bReplaceWithLast )
382         return;
383 
384     removeStatusListener( m_aCommandURL );
385 
386     auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, m_sModuleName);
387     OUString aRealCommand( vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties) );
388     m_aCommandURL = aRealCommand.isEmpty() ? rCommand : aRealCommand;
389     addStatusListener( m_aCommandURL );
390 
391     ToolBox* pToolBox = nullptr;
392     ToolBoxItemId nId;
393     if ( getToolboxId( nId, &pToolBox ) )
394     {
395         pToolBox->SetItemCommand( nId, rCommand );
396         pToolBox->SetHelpText( nId, OUString() ); // Will retrieve the new one from help.
397         pToolBox->SetItemText(nId, vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
398         pToolBox->SetQuickHelpText(nId, vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, m_xFrame));
399 
400         Image aImage = vcl::CommandInfoProvider::GetImageForCommand(rCommand, m_xFrame, pToolBox->GetImageSize());
401         if ( !!aImage )
402             pToolBox->SetItemImage( nId, aImage );
403     }
404 }
405 
getDropDownStyle() const406 ToolBoxItemBits GenericPopupToolbarController::getDropDownStyle() const
407 {
408     return m_bSplitButton ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY;
409 }
410 
411 class SaveToolbarController : public cppu::ImplInheritanceHelper< PopupMenuToolbarController,
412                                                                   css::frame::XSubToolbarController,
413                                                                   css::util::XModifyListener >
414 {
415 public:
416     explicit SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
417 
418     // XInitialization
419     virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
420 
421     // XSubToolbarController
422     // Make ToolBarManager ask our controller for updated image, in case of icon theme change.
423     virtual sal_Bool SAL_CALL opensSubToolbar() override;
424     virtual OUString SAL_CALL getSubToolbarName() override;
425     virtual void SAL_CALL functionSelected( const OUString& aCommand ) override;
426     virtual void SAL_CALL updateImage() override;
427 
428     // XStatusListener
429     virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
430 
431     // XModifyListener
432     virtual void SAL_CALL modified( const css::lang::EventObject& rEvent ) override;
433 
434     // XEventListener
435     virtual void SAL_CALL disposing( const css::lang::EventObject& rEvent ) override;
436 
437     // XComponent
438     virtual void SAL_CALL dispose() override;
439 
440     // XServiceInfo
441     virtual OUString SAL_CALL getImplementationName() override;
442     virtual sal_Bool SAL_CALL supportsService( OUString const & rServiceName ) override;
443     virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
444 
445 private:
446     bool m_bReadOnly;
447     bool m_bModified;
448     css::uno::Reference< css::frame::XStorable > m_xStorable;
449     css::uno::Reference< css::util::XModifiable > m_xModifiable;
450 };
451 
SaveToolbarController(const css::uno::Reference<css::uno::XComponentContext> & rxContext)452 SaveToolbarController::SaveToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
453     : ImplInheritanceHelper( rxContext, ".uno:SaveAsMenu" )
454     , m_bReadOnly( false )
455     , m_bModified( false )
456 {
457 }
458 
initialize(const css::uno::Sequence<css::uno::Any> & aArguments)459 void SaveToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
460 {
461     PopupMenuToolbarController::initialize( aArguments );
462 
463     ToolBox* pToolBox = nullptr;
464     ToolBoxItemId nId;
465     if ( !getToolboxId( nId, &pToolBox ) )
466         return;
467 
468     css::uno::Reference< css::frame::XController > xController = m_xFrame->getController();
469     if ( xController.is() )
470         m_xModifiable.set( xController->getModel(), css::uno::UNO_QUERY );
471 
472     if ( m_xModifiable.is() && pToolBox->GetItemCommand( nId ) == m_aCommandURL )
473         // Will also enable the save as only mode.
474         m_xStorable.set( m_xModifiable, css::uno::UNO_QUERY );
475     else if ( !m_xModifiable.is() )
476         // Can be in table/query design.
477         m_xModifiable.set( xController, css::uno::UNO_QUERY );
478     else
479         // Simple save button, without the dropdown.
480         pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~ ToolBoxItemBits::DROPDOWN );
481 
482     if ( m_xModifiable.is() )
483     {
484         m_xModifiable->addModifyListener( this );
485         modified( css::lang::EventObject() );
486     }
487 }
488 
opensSubToolbar()489 sal_Bool SaveToolbarController::opensSubToolbar()
490 {
491     return true;
492 }
493 
getSubToolbarName()494 OUString SaveToolbarController::getSubToolbarName()
495 {
496     return OUString();
497 }
498 
functionSelected(const OUString &)499 void SaveToolbarController::functionSelected( const OUString& /*aCommand*/ )
500 {
501 }
502 
updateImage()503 void SaveToolbarController::updateImage()
504 {
505     SolarMutexGuard aGuard;
506     ToolBox* pToolBox = nullptr;
507     ToolBoxItemId nId;
508     if ( !getToolboxId( nId, &pToolBox ) )
509         return;
510 
511     vcl::ImageType eImageType = pToolBox->GetImageSize();
512 
513     Image aImage;
514 
515     if ( m_bReadOnly )
516     {
517         aImage = vcl::CommandInfoProvider::GetImageForCommand(".uno:SaveAs", m_xFrame, eImageType);
518     }
519     else if ( m_bModified )
520     {
521         if (eImageType == vcl::ImageType::Size26)
522             aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_LARGE);
523         else if (eImageType == vcl::ImageType::Size32)
524             aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_EXTRALARGE);
525         else
526             aImage = Image(StockImage::Yes, BMP_SAVEMODIFIED_SMALL);
527     }
528 
529     if ( !aImage )
530         aImage = vcl::CommandInfoProvider::GetImageForCommand(m_aCommandURL, m_xFrame, eImageType);
531 
532     if ( !!aImage )
533         pToolBox->SetItemImage( nId, aImage );
534 }
535 
statusChanged(const css::frame::FeatureStateEvent & rEvent)536 void SaveToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
537 {
538     ToolBox* pToolBox = nullptr;
539     ToolBoxItemId nId;
540     if ( !getToolboxId( nId, &pToolBox ) )
541         return;
542 
543     bool bLastReadOnly = m_bReadOnly;
544     m_bReadOnly = m_xStorable.is() && m_xStorable->isReadonly();
545     if ( bLastReadOnly != m_bReadOnly )
546     {
547         OUString sCommand = m_bReadOnly ? OUString( ".uno:SaveAs" ) : m_aCommandURL;
548         auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sCommand,
549             vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame));
550         pToolBox->SetQuickHelpText( nId,
551             vcl::CommandInfoProvider::GetTooltipForCommand(sCommand, aProperties, m_xFrame) );
552         pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) & ~( m_bReadOnly ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY ) );
553         pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) |  ( m_bReadOnly ? ToolBoxItemBits::DROPDOWNONLY : ToolBoxItemBits::DROPDOWN ) );
554         updateImage();
555     }
556 
557     if ( !m_bReadOnly )
558         pToolBox->EnableItem( nId, rEvent.IsEnabled );
559 }
560 
modified(const css::lang::EventObject &)561 void SaveToolbarController::modified( const css::lang::EventObject& /*rEvent*/ )
562 {
563     bool bLastModified = m_bModified;
564     m_bModified = m_xModifiable->isModified();
565     if ( bLastModified != m_bModified )
566         updateImage();
567 }
568 
disposing(const css::lang::EventObject & rEvent)569 void SaveToolbarController::disposing( const css::lang::EventObject& rEvent )
570 {
571     if ( rEvent.Source == m_xModifiable )
572     {
573         m_xModifiable.clear();
574         m_xStorable.clear();
575     }
576     else
577         PopupMenuToolbarController::disposing( rEvent );
578 }
579 
dispose()580 void SaveToolbarController::dispose()
581 {
582     PopupMenuToolbarController::dispose();
583     if ( m_xModifiable.is() )
584     {
585         m_xModifiable->removeModifyListener( this );
586         m_xModifiable.clear();
587     }
588     m_xStorable.clear();
589 }
590 
getImplementationName()591 OUString SaveToolbarController::getImplementationName()
592 {
593     return "com.sun.star.comp.framework.SaveToolbarController";
594 }
595 
supportsService(OUString const & rServiceName)596 sal_Bool SaveToolbarController::supportsService( OUString const & rServiceName )
597 {
598     return cppu::supportsService( this, rServiceName );
599 }
600 
getSupportedServiceNames()601 css::uno::Sequence< OUString > SaveToolbarController::getSupportedServiceNames()
602 {
603     return {"com.sun.star.frame.ToolbarController"};
604 }
605 
606 class NewToolbarController : public cppu::ImplInheritanceHelper<PopupMenuToolbarController, css::frame::XSubToolbarController>
607 {
608 public:
609     explicit NewToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
610 
611     // XServiceInfo
612     OUString SAL_CALL getImplementationName() override;
613 
614     virtual sal_Bool SAL_CALL supportsService(OUString const & rServiceName) override;
615 
616     css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
617 
618     void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
619 
620     // XSubToolbarController
621     // Make ToolBarManager ask our controller for updated image, in case of icon theme change.
opensSubToolbar()622     sal_Bool SAL_CALL opensSubToolbar() override { return true; }
getSubToolbarName()623     OUString SAL_CALL getSubToolbarName() override { return OUString(); }
functionSelected(const OUString &)624     void SAL_CALL functionSelected( const OUString& ) override {}
625     void SAL_CALL updateImage() override;
626 
627 private:
628     void functionExecuted( const OUString &rCommand ) override;
629     void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
630     void SAL_CALL execute( sal_Int16 KeyModifier ) override;
631     sal_uInt16 getMenuIdForCommand( std::u16string_view rCommand );
632 
633     sal_uInt16 m_nMenuId;
634 };
635 
NewToolbarController(const css::uno::Reference<css::uno::XComponentContext> & xContext)636 NewToolbarController::NewToolbarController(
637     const css::uno::Reference< css::uno::XComponentContext >& xContext )
638     : ImplInheritanceHelper( xContext )
639     , m_nMenuId( 0 )
640 {
641 }
642 
getImplementationName()643 OUString NewToolbarController::getImplementationName()
644 {
645     return "org.apache.openoffice.comp.framework.NewToolbarController";
646 }
647 
supportsService(OUString const & rServiceName)648 sal_Bool NewToolbarController::supportsService(OUString const & rServiceName)
649 {
650     return cppu::supportsService( this, rServiceName );
651 }
652 
getSupportedServiceNames()653 css::uno::Sequence<OUString> NewToolbarController::getSupportedServiceNames()
654 {
655     return {"com.sun.star.frame.ToolbarController"};
656 }
657 
initialize(const css::uno::Sequence<css::uno::Any> & aArguments)658 void SAL_CALL NewToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
659 {
660     PopupMenuToolbarController::initialize( aArguments );
661 
662     osl::MutexGuard aGuard( m_aMutex );
663     createPopupMenuController();
664 }
665 
statusChanged(const css::frame::FeatureStateEvent & rEvent)666 void SAL_CALL NewToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
667 {
668     if ( rEvent.IsEnabled )
669     {
670         OUString aState;
671         rEvent.State >>= aState;
672         try
673         {
674             // set the image even if the state is not a string
675             // the toolbar item command will be used as a fallback
676             functionExecuted( aState );
677         }
678         catch (const css::ucb::CommandFailedException&)
679         {
680         }
681         catch (const css::ucb::ContentCreationException&)
682         {
683         }
684     }
685 
686     enable( rEvent.IsEnabled );
687 }
688 
execute(sal_Int16)689 void SAL_CALL NewToolbarController::execute( sal_Int16 /*KeyModifier*/ )
690 {
691     osl::MutexGuard aGuard( m_aMutex );
692 
693     OUString aURL, aTarget;
694     if ( m_xPopupMenu.is() && m_nMenuId )
695     {
696         // TODO investigate how to wrap Get/SetUserValue in css::awt::XMenu
697         SolarMutexGuard aSolarMutexGuard;
698         Menu* pVclMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu )->GetMenu();
699         aURL = pVclMenu->GetItemCommand( m_nMenuId );
700 
701         MenuAttributes* pMenuAttributes( static_cast<MenuAttributes*>( pVclMenu->GetUserValue( m_nMenuId ) ) );
702         if ( pMenuAttributes )
703             aTarget = pMenuAttributes->aTargetFrame;
704         else
705             aTarget = "_default";
706     }
707     else
708         aURL = m_aCommandURL;
709 
710     css::uno::Sequence< css::beans::PropertyValue > aArgs( 1 );
711     aArgs[0].Name = "Referer";
712     aArgs[0].Value <<= OUString( "private:user" );
713 
714     dispatchCommand( aURL, aArgs, aTarget );
715 }
716 
functionExecuted(const OUString & rCommand)717 void NewToolbarController::functionExecuted( const OUString &rCommand )
718 {
719     m_nMenuId = getMenuIdForCommand( rCommand );
720     updateImage();
721 }
722 
getMenuIdForCommand(std::u16string_view rCommand)723 sal_uInt16 NewToolbarController::getMenuIdForCommand( std::u16string_view rCommand )
724 {
725     if ( m_xPopupMenu.is() && !rCommand.empty() )
726     {
727         Menu* pVclMenu( comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu )->GetMenu() );
728         sal_uInt16 nCount = pVclMenu->GetItemCount();
729         for ( sal_uInt16 n = 0; n < nCount; ++n )
730         {
731             sal_uInt16 nId = pVclMenu->GetItemId( n );
732             OUString aCmd( pVclMenu->GetItemCommand( nId ) );
733 
734             // match even if the menu command is more detailed
735             // (maybe an additional query) #i28667#
736             if ( aCmd.match( rCommand ) )
737                 return nId;
738         }
739     }
740 
741     return 0;
742 }
743 
updateImage()744 void SAL_CALL NewToolbarController::updateImage()
745 {
746     SolarMutexGuard aSolarLock;
747     VclPtr< ToolBox> pToolBox = static_cast< ToolBox* >( VCLUnoHelper::GetWindow( getParent() ) );
748     if ( !pToolBox )
749         return;
750 
751     OUString aURL, aImageId;
752     if ( m_xPopupMenu.is() && m_nMenuId )
753     {
754         Menu* pVclMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( m_xPopupMenu )->GetMenu();
755         aURL = pVclMenu->GetItemCommand( m_nMenuId );
756 
757         MenuAttributes* pMenuAttributes( static_cast<MenuAttributes*>( pVclMenu->GetUserValue( m_nMenuId ) ) );
758         if ( pMenuAttributes )
759             aImageId = pMenuAttributes->aImageId;
760     }
761     else
762         aURL = m_aCommandURL;
763 
764     INetURLObject aURLObj( aImageId.isEmpty() ? aURL : aImageId );
765     vcl::ImageType eImageType( pToolBox->GetImageSize() );
766     Image aImage = SvFileInformationManager::GetImageNoDefault( aURLObj, eImageType );
767     if ( !aImage )
768         aImage = vcl::CommandInfoProvider::GetImageForCommand( aURL, m_xFrame, eImageType );
769 
770     if ( !aImage )
771         return;
772 
773     pToolBox->SetItemImage( m_nToolBoxId, aImage );
774 }
775 
776 }
777 
778 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_framework_GenericPopupToolbarController_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const & args)779 com_sun_star_comp_framework_GenericPopupToolbarController_get_implementation(
780     css::uno::XComponentContext *context,
781     css::uno::Sequence<css::uno::Any> const &args)
782 {
783     return cppu::acquire(new GenericPopupToolbarController(context, args));
784 }
785 
786 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_framework_SaveToolbarController_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)787 com_sun_star_comp_framework_SaveToolbarController_get_implementation(
788     css::uno::XComponentContext *context,
789     css::uno::Sequence<css::uno::Any> const &)
790 {
791     return cppu::acquire(new SaveToolbarController(context));
792 }
793 
794 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
org_apache_openoffice_comp_framework_NewToolbarController_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)795 org_apache_openoffice_comp_framework_NewToolbarController_get_implementation(
796     css::uno::XComponentContext *context,
797     css::uno::Sequence<css::uno::Any> const &)
798 {
799     return cppu::acquire(new NewToolbarController(context));
800 }
801 
802 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
803