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