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