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