xref: /trunk/main/svtools/source/uno/contextmenuhelper.cxx (revision d8dff77764cb74143fabc617dc8ee25d946bae78)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_svtools.hxx"
24 
25 #include <svtools/contextmenuhelper.hxx>
26 #include <svtools/menuoptions.hxx>
27 #include <svtools/miscopt.hxx>
28 
29 #include <com/sun/star/frame/XDispatch.hpp>
30 #include <com/sun/star/frame/XDispatchProvider.hpp>
31 #include <com/sun/star/frame/XModuleManager.hpp>
32 #include <com/sun/star/frame/XStatusListener.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp>
35 #include <com/sun/star/ui/XUIConfigurationManager.hpp>
36 #include <com/sun/star/ui/XModuleUIConfigurationManagerSupplier.hpp>
37 #include <com/sun/star/ui/ImageType.hpp>
38 #include <com/sun/star/beans/PropertyValue.hpp>
39 
40 #include <osl/conditn.hxx>
41 #include <cppuhelper/weak.hxx>
42 #include <comphelper/processfactory.hxx>
43 #include <vos/mutex.hxx>
44 #include <vcl/svapp.hxx>
45 #include <vcl/image.hxx>
46 #include <toolkit/helper/vclunohelper.hxx>
47 #include <toolkit/awt/vclxwindow.hxx>
48 #include <toolkit/awt/vclxmenu.hxx>
49 
50 using namespace ::com::sun::star;
51 
52 namespace svt
53 {
54 
55 // internal helper class to retrieve status updates
56 class StateEventHelper : public ::com::sun::star::frame::XStatusListener,
57                          public ::cppu::OWeakObject
58 {
59     public:
60         StateEventHelper( const uno::Reference< frame::XDispatchProvider >& xDispatchProvider,
61                           const uno::Reference< util::XURLTransformer >& xURLTransformer,
62                           const rtl::OUString& aCommandURL );
63         virtual ~StateEventHelper();
64 
65         bool isCommandEnabled();
66 
67         // XInterface
68         virtual uno::Any SAL_CALL queryInterface( const uno::Type& aType ) throw ( uno::RuntimeException);
69         virtual void SAL_CALL acquire() throw ();
70         virtual void SAL_CALL release() throw ();
71 
72         // XEventListener
73         virtual void SAL_CALL disposing(const lang::EventObject& Source) throw( uno::RuntimeException );
74 
75         // XStatusListener
76         virtual void SAL_CALL statusChanged(const frame::FeatureStateEvent& Event) throw( uno::RuntimeException );
77 
78     private:
79         StateEventHelper();
80         StateEventHelper( const StateEventHelper& );
81         StateEventHelper& operator=( const StateEventHelper& );
82 
83         bool                                       m_bCurrentCommandEnabled;
84         ::rtl::OUString                            m_aCommandURL;
85         uno::Reference< frame::XDispatchProvider > m_xDispatchProvider;
86         uno::Reference< util::XURLTransformer >    m_xURLTransformer;
87         osl::Condition                             m_aCondition;
88 };
89 
90 StateEventHelper::StateEventHelper(
91     const uno::Reference< frame::XDispatchProvider >& xDispatchProvider,
92     const uno::Reference< util::XURLTransformer >& xURLTransformer,
93     const rtl::OUString& rCommandURL ) :
94     m_bCurrentCommandEnabled( true ),
95     m_aCommandURL( rCommandURL ),
96     m_xDispatchProvider( xDispatchProvider ),
97     m_xURLTransformer( xURLTransformer )
98 {
99     m_aCondition.reset();
100 }
101 
102 StateEventHelper::~StateEventHelper()
103 {}
104 
105 uno::Any SAL_CALL StateEventHelper::queryInterface(
106     const uno::Type& aType )
107 throw ( uno::RuntimeException )
108 {
109     uno::Any a = ::cppu::queryInterface(
110                 aType,
111                 SAL_STATIC_CAST( XStatusListener*, this ));
112 
113     if( a.hasValue() )
114         return a;
115 
116     return ::cppu::OWeakObject::queryInterface( aType );
117 }
118 
119 void SAL_CALL StateEventHelper::acquire()
120 throw ()
121 {
122     ::cppu::OWeakObject::acquire();
123 }
124 
125 void SAL_CALL StateEventHelper::release()
126 throw ()
127 {
128     ::cppu::OWeakObject::release();
129 }
130 
131 void SAL_CALL StateEventHelper::disposing(
132     const lang::EventObject& )
133 throw ( uno::RuntimeException )
134 {
135     vos::OGuard aSolarGuard( Application::GetSolarMutex() );
136     m_xDispatchProvider.clear();
137     m_xURLTransformer.clear();
138     m_aCondition.set();
139 }
140 
141 void SAL_CALL StateEventHelper::statusChanged(
142     const frame::FeatureStateEvent& Event )
143 throw ( uno::RuntimeException )
144 {
145     vos::OGuard aSolarGuard( Application::GetSolarMutex() );
146     m_bCurrentCommandEnabled = Event.IsEnabled;
147     m_aCondition.set();
148 }
149 
150 bool StateEventHelper::isCommandEnabled()
151 {
152     // Be sure that we cannot die during condition wait
153     uno::Reference< frame::XStatusListener > xSelf(
154         SAL_STATIC_CAST( frame::XStatusListener*, this ));
155 
156     uno::Reference< frame::XDispatch > xDispatch;
157     util::URL                          aTargetURL;
158     {
159         vos::OGuard aSolarGuard( Application::GetSolarMutex() );
160         if ( m_xDispatchProvider.is() && m_xURLTransformer.is() )
161         {
162             ::rtl::OUString aSelf( RTL_CONSTASCII_USTRINGPARAM( "_self" ));
163 
164             aTargetURL.Complete = m_aCommandURL;
165             m_xURLTransformer->parseStrict( aTargetURL );
166 
167             try
168             {
169                 xDispatch = m_xDispatchProvider->queryDispatch( aTargetURL, aSelf, 0 );
170             }
171             catch ( uno::RuntimeException& )
172             {
173                 throw;
174             }
175             catch ( uno::Exception& )
176             {
177             }
178         }
179     }
180 
181     bool bResult( false );
182     if ( xDispatch.is() )
183     {
184         try
185         {
186             // add/remove ourself to retrieve status by callback
187             xDispatch->addStatusListener( xSelf, aTargetURL );
188             xDispatch->removeStatusListener( xSelf, aTargetURL );
189 
190             // wait for answer
191             m_aCondition.wait();
192         }
193         catch ( uno::RuntimeException& )
194         {
195             throw;
196         }
197         catch ( uno::Exception& )
198         {
199         }
200 
201         vos::OGuard aSolarGuard( Application::GetSolarMutex() );
202         bResult = m_bCurrentCommandEnabled;
203     }
204 
205     return bResult;
206 }
207 
208 /*************************************************************************/
209 
210 struct ExecuteInfo
211 {
212     uno::Reference< frame::XDispatch >    xDispatch;
213     util::URL                             aTargetURL;
214     uno::Sequence< beans::PropertyValue > aArgs;
215 };
216 
217 static const PopupMenu* lcl_FindPopupFromItemId( const PopupMenu* pPopupMenu, sal_uInt16 nItemId )
218 {
219     if ( pPopupMenu )
220     {
221         sal_uInt16 nCount = pPopupMenu->GetItemCount();
222         for ( sal_uInt16 i = 0; i < nCount; i++ )
223         {
224             sal_uInt16 nId = pPopupMenu->GetItemId( i );
225             if ( nId == nItemId )
226                 return pPopupMenu;
227             else
228             {
229                 const PopupMenu* pResult( 0 );
230 
231                 const PopupMenu* pSubPopup = pPopupMenu->GetPopupMenu( i );
232                 if ( pPopupMenu )
233                     pResult = lcl_FindPopupFromItemId( pSubPopup, nItemId );
234                 if ( pResult != 0 )
235                     return pResult;
236             }
237         }
238     }
239 
240     return NULL;
241 }
242 
243 static ::rtl::OUString lcl_GetItemCommandRecursive( const PopupMenu* pPopupMenu, sal_uInt16 nItemId )
244 {
245     const PopupMenu* pPopup = lcl_FindPopupFromItemId( pPopupMenu, nItemId );
246     if ( pPopup )
247         return pPopup->GetItemCommand( nItemId );
248     else
249         return ::rtl::OUString();
250 }
251 
252 /*************************************************************************/
253 
254 ContextMenuHelper::ContextMenuHelper(
255     const uno::Reference< frame::XFrame >& xFrame,
256     bool bAutoRefresh ) :
257     m_xWeakFrame( xFrame ),
258     m_aSelf( RTL_CONSTASCII_USTRINGPARAM( "_self" )),
259     m_bAutoRefresh( bAutoRefresh ),
260     m_bUICfgMgrAssociated( false )
261 {
262 }
263 
264 ContextMenuHelper::~ContextMenuHelper()
265 {
266 }
267 
268 void
269 ContextMenuHelper::completeAndExecute(
270     const Point& aPos,
271     PopupMenu& rPopupMenu )
272 {
273     vos::OGuard aSolarGuard( Application::GetSolarMutex() );
274 
275     associateUIConfigurationManagers();
276     completeMenuProperties( &rPopupMenu );
277     executePopupMenu( aPos, &rPopupMenu );
278     resetAssociations();
279 }
280 
281 void
282 ContextMenuHelper::completeAndExecute(
283     const Point& aPos,
284     const uno::Reference< awt::XPopupMenu >& xPopupMenu )
285 {
286     vos::OGuard aSolarGuard( Application::GetSolarMutex() );
287 
288     VCLXMenu* pXMenu = VCLXMenu::GetImplementation( xPopupMenu );
289     if ( pXMenu )
290     {
291         PopupMenu* pPopupMenu = dynamic_cast< PopupMenu* >( pXMenu->GetMenu() );
292         // as dynamic_cast can return zero check pointer
293         if ( pPopupMenu )
294         {
295             associateUIConfigurationManagers();
296             completeMenuProperties( pPopupMenu );
297             executePopupMenu( aPos, pPopupMenu );
298             resetAssociations();
299         }
300     }
301 }
302 
303 uno::Reference< awt::XPopupMenu >
304 ContextMenuHelper::create(
305     const ::rtl::OUString& /*aPopupMenuResourceId*/ )
306 {
307     // NOT IMPLEMENTED YET!
308     return uno::Reference< awt::XPopupMenu >();
309 }
310 
311 bool
312 ContextMenuHelper::createAndExecute(
313     const Point& /*aPos*/,
314     const ::rtl::OUString& /*aPopupMenuResourceId*/ )
315 {
316     // NOT IMPLEMENTED YET!
317     return false;
318 }
319 
320 // private member
321 
322 void
323 ContextMenuHelper::executePopupMenu(
324     const Point& rPos,
325     PopupMenu* pMenu )
326 {
327     if ( pMenu )
328     {
329         uno::Reference< frame::XFrame > xFrame( m_xWeakFrame );
330         if ( xFrame.is() )
331         {
332             uno::Reference< awt::XWindow > xWindow( xFrame->getContainerWindow() );
333             if ( xWindow.is() )
334             {
335                 Window* pParent = VCLUnoHelper::GetWindow( xWindow );
336                 sal_uInt16 nResult = pMenu->Execute( pParent, rPos );
337 
338                 if ( nResult > 0 )
339                 {
340                     ::rtl::OUString aCommand = lcl_GetItemCommandRecursive( pMenu, nResult );
341                     if ( aCommand.getLength() > 0 )
342                         dispatchCommand( xFrame, aCommand );
343                 }
344             }
345         }
346     }
347 }
348 
349 bool
350 ContextMenuHelper::dispatchCommand(
351     const uno::Reference< ::frame::XFrame >& rFrame,
352     const ::rtl::OUString& aCommandURL )
353 {
354     if ( !m_xURLTransformer.is() )
355     {
356         m_xURLTransformer = uno::Reference< util::XURLTransformer >(
357             ::comphelper::getProcessServiceFactory()->createInstance(
358                 rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
359                     "com.sun.star.util.URLTransformer" ))),
360             uno::UNO_QUERY );
361     }
362 
363     util::URL aTargetURL;
364     uno::Reference< frame::XDispatch > xDispatch;
365     if ( m_xURLTransformer.is() )
366     {
367         aTargetURL.Complete = aCommandURL;
368         m_xURLTransformer->parseStrict( aTargetURL );
369 
370         uno::Reference< frame::XDispatchProvider > xDispatchProvider(
371             rFrame, uno::UNO_QUERY );
372         if ( xDispatchProvider.is() )
373         {
374             try
375             {
376                 xDispatch = xDispatchProvider->queryDispatch( aTargetURL, m_aSelf, 0 );
377             }
378             catch ( uno::RuntimeException& )
379             {
380                 throw;
381             }
382             catch ( uno::Exception& )
383             {
384             }
385         }
386     }
387 
388     if ( xDispatch.is() )
389     {
390         ExecuteInfo* pExecuteInfo = new ExecuteInfo;
391         pExecuteInfo->xDispatch    = xDispatch;
392         pExecuteInfo->aTargetURL   = aTargetURL;
393         pExecuteInfo->aArgs        = m_aDefaultArgs;
394 
395         Application::PostUserEvent( STATIC_LINK(0, ContextMenuHelper , ExecuteHdl_Impl), pExecuteInfo );
396         return true;
397     }
398 
399     return false;
400 }
401 
402 // retrieves and stores references to our user-interface
403 // configuration managers, like image manager, UI command
404 // description manager.
405 bool
406 ContextMenuHelper::associateUIConfigurationManagers()
407 {
408     uno::Reference< frame::XFrame > xFrame( m_xWeakFrame );
409     if ( !m_bUICfgMgrAssociated && xFrame.is() )
410     {
411         // clear current state
412         m_xDocImageMgr.clear();
413         m_xModuleImageMgr.clear();
414         m_xUICommandLabels.clear();
415 
416         try
417         {
418             uno::Reference < frame::XController > xController;
419             uno::Reference < frame::XModel > xModel;
420             xController = xFrame->getController();
421             if ( xController.is() )
422                 xModel = xController->getModel();
423 
424             if ( xModel.is() )
425             {
426                 // retrieve document image manager form model
427                 uno::Reference< ui::XUIConfigurationManagerSupplier > xSupplier( xModel, uno::UNO_QUERY );
428                 if ( xSupplier.is() )
429                 {
430                     uno::Reference< ui::XUIConfigurationManager > xDocUICfgMgr(
431                         xSupplier->getUIConfigurationManager(), uno::UNO_QUERY );
432                     m_xDocImageMgr = uno::Reference< ui::XImageManager >(
433                         xDocUICfgMgr->getImageManager(), uno::UNO_QUERY );
434                 }
435             }
436 
437             uno::Reference< frame::XModuleManager > xModuleManager(
438                 ::comphelper::getProcessServiceFactory()->createInstance(
439                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
440                         "com.sun.star.frame.ModuleManager" ))),
441                 uno::UNO_QUERY );
442 
443             uno::Reference< ui::XImageManager > xModuleImageManager;
444             rtl::OUString                       aModuleId;
445             if ( xModuleManager.is() )
446             {
447                 // retrieve module image manager
448                 aModuleId = xModuleManager->identify( xFrame );
449 
450                 uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier(
451                     ::comphelper::getProcessServiceFactory()->createInstance(
452                         rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
453                             "com.sun.star.ui.ModuleUIConfigurationManagerSupplier" ))),
454                         uno::UNO_QUERY );
455                 if ( xModuleCfgMgrSupplier.is() )
456                 {
457                     uno::Reference< ui::XUIConfigurationManager > xUICfgMgr(
458                         xModuleCfgMgrSupplier->getUIConfigurationManager( aModuleId ));
459                     if ( xUICfgMgr.is() )
460                     {
461                         m_xModuleImageMgr = uno::Reference< ui::XImageManager >(
462                             xUICfgMgr->getImageManager(), uno::UNO_QUERY );
463                     }
464                 }
465             }
466 
467             uno::Reference< container::XNameAccess > xNameAccess(
468                 ::comphelper::getProcessServiceFactory()->createInstance(
469                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
470                         "com.sun.star.frame.UICommandDescription" ))),
471                     uno::UNO_QUERY );
472             if ( xNameAccess.is() )
473             {
474                 try
475                 {
476                     uno::Any a = xNameAccess->getByName( aModuleId );
477                     a >>= m_xUICommandLabels;
478                 }
479                 catch ( container::NoSuchElementException& )
480                 {
481                 }
482             }
483         }
484         catch ( uno::RuntimeException& )
485         {
486             throw;
487         }
488         catch ( uno::Exception& )
489         {
490             m_bUICfgMgrAssociated = true;
491             return false;
492         }
493         m_bUICfgMgrAssociated = true;
494     }
495 
496     return true;
497 }
498 
499 Image
500 ContextMenuHelper::getImageFromCommandURL(
501     const ::rtl::OUString& aCmdURL,
502     bool                   bHiContrast ) const
503 {
504     Image     aImage;
505     sal_Int16 nImageType( ui::ImageType::COLOR_NORMAL|
506                           ui::ImageType::SIZE_DEFAULT );
507     if ( bHiContrast )
508         nImageType |= ui::ImageType::COLOR_HIGHCONTRAST;
509 
510     uno::Sequence< uno::Reference< graphic::XGraphic > > aGraphicSeq;
511     uno::Sequence< ::rtl::OUString > aImageCmdSeq( 1 );
512     aImageCmdSeq[0] = aCmdURL;
513 
514     if ( m_xDocImageMgr.is() )
515     {
516         try
517         {
518             aGraphicSeq = m_xDocImageMgr->getImages( nImageType, aImageCmdSeq );
519             uno::Reference< graphic::XGraphic > xGraphic = aGraphicSeq[0];
520             aImage = Image( xGraphic );
521 
522             if ( !!aImage )
523                 return aImage;
524         }
525         catch ( uno::RuntimeException& )
526         {
527             throw;
528         }
529         catch ( uno::Exception& )
530         {
531         }
532     }
533 
534     if ( m_xModuleImageMgr.is() )
535     {
536         try
537         {
538             aGraphicSeq = m_xModuleImageMgr->getImages( nImageType, aImageCmdSeq );
539             uno::Reference< ::com::sun::star::graphic::XGraphic > xGraphic = aGraphicSeq[0];
540         aImage = Image( xGraphic );
541 
542             if ( !!aImage )
543                 return aImage;
544         }
545         catch ( uno::RuntimeException& )
546         {
547             throw;
548         }
549         catch ( uno::Exception& )
550         {
551         }
552     }
553 
554     return aImage;
555 }
556 
557 rtl::OUString
558 ContextMenuHelper::getLabelFromCommandURL(
559     const ::rtl::OUString& aCmdURL ) const
560 {
561     ::rtl::OUString aLabel;
562 
563     if ( m_xUICommandLabels.is() )
564     {
565         try
566         {
567             if ( aCmdURL.getLength() > 0 )
568             {
569                 rtl::OUString aStr;
570                 uno::Sequence< beans::PropertyValue > aPropSeq;
571                 uno::Any a( m_xUICommandLabels->getByName( aCmdURL ));
572                 if ( a >>= aPropSeq )
573                 {
574                     for ( sal_Int32 i = 0; i < aPropSeq.getLength(); i++ )
575                     {
576                         if ( aPropSeq[i].Name.equalsAscii( "Label" ))
577                         {
578                             aPropSeq[i].Value >>= aStr;
579                             break;
580                         }
581                     }
582                 }
583                 aLabel = aStr;
584             }
585         }
586         catch ( uno::RuntimeException& )
587         {
588         }
589         catch ( uno::Exception& )
590         {
591         }
592     }
593 
594     return aLabel;
595 }
596 
597 void
598 ContextMenuHelper::completeMenuProperties(
599     Menu* pMenu )
600 {
601     // Retrieve some settings necessary to display complete context
602     // menu correctly.
603     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
604     bool  bShowMenuImages( rSettings.GetUseImagesInMenus() );
605     bool  bIsHiContrast( rSettings.GetHighContrastMode() );
606 
607     if ( pMenu )
608     {
609         uno::Reference< frame::XFrame > xFrame( m_xWeakFrame );
610         uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, uno::UNO_QUERY );
611 
612         if ( !m_xURLTransformer.is() )
613         {
614             m_xURLTransformer = uno::Reference< util::XURLTransformer >(
615                 ::comphelper::getProcessServiceFactory()->createInstance(
616                     rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
617                         "com.sun.star.util.URLTransformer" ))),
618                 uno::UNO_QUERY );
619         }
620 
621         for ( sal_uInt16 nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
622         {
623             sal_uInt16 nId        = pMenu->GetItemId( nPos );
624             PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nId );
625             if ( pPopupMenu )
626                 completeMenuProperties( pPopupMenu );
627             if ( pMenu->GetItemType( nPos ) != MENUITEM_SEPARATOR )
628             {
629                 ::rtl::OUString aCmdURL( pMenu->GetItemCommand( nId ));
630 
631                 if ( bShowMenuImages )
632                 {
633                     Image aImage;
634                     if ( aCmdURL.getLength() > 0 )
635                         aImage = getImageFromCommandURL( aCmdURL, bIsHiContrast );
636                     pMenu->SetItemImage( nId, aImage );
637                 }
638                 else
639                     pMenu->SetItemImage( nId, Image() );
640 
641                 if ( pMenu->GetItemText( nId ).Len() == 0 )
642                 {
643                     ::rtl::OUString aLabel( getLabelFromCommandURL( aCmdURL ));
644                     pMenu->SetItemText( nId, aLabel );
645                 }
646 
647                 // Use helper to retrieve state of the command URL
648                 StateEventHelper* pHelper = new StateEventHelper(
649                                                     xDispatchProvider,
650                                                     m_xURLTransformer,
651                                                     aCmdURL );
652 
653                 uno::Reference< frame::XStatusListener > xHelper( pHelper );
654                 pMenu->EnableItem( nId, pHelper->isCommandEnabled() );
655             }
656         }
657     }
658 }
659 
660 IMPL_STATIC_LINK_NOINSTANCE( ContextMenuHelper, ExecuteHdl_Impl, ExecuteInfo*, pExecuteInfo )
661 {
662     // Release solar mutex to prevent deadlocks with clipboard thread
663     const sal_uInt32 nRef = Application::ReleaseSolarMutex();
664     try
665     {
666         // Asynchronous execution as this can lead to our own destruction while we are
667         // on the stack. Stack unwinding would access the destroyed context menu.
668         pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
669     }
670     catch ( uno::Exception& )
671     {
672     }
673 
674     // Acquire solar mutex again
675     Application::AcquireSolarMutex( nRef );
676     delete pExecuteInfo;
677     return 0;
678 }
679 
680 } // namespace svt
681 
682 /* vim: set noet sw=4 ts=4: */
683