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_sfx2.hxx" 26 27 #include <tools/debug.hxx> 28 #include <svl/eitem.hxx> 29 #include <svl/stritem.hxx> 30 #include <svl/intitem.hxx> 31 #include <svl/itemset.hxx> 32 #include <svl/visitem.hxx> 33 #include <svtools/javacontext.hxx> 34 #include <svl/itempool.hxx> 35 #include <tools/urlobj.hxx> 36 #include <com/sun/star/util/XURLTransformer.hpp> 37 #include <com/sun/star/frame/XController.hpp> 38 #include <com/sun/star/frame/XFrameActionListener.hpp> 39 #include <com/sun/star/frame/XComponentLoader.hpp> 40 #include <com/sun/star/frame/XFrame.hpp> 41 #include <com/sun/star/frame/FrameActionEvent.hpp> 42 #include <com/sun/star/frame/FrameAction.hpp> 43 #include <com/sun/star/frame/status/ItemStatus.hpp> 44 #include <com/sun/star/frame/status/ItemState.hpp> 45 #include <com/sun/star/frame/DispatchResultState.hpp> 46 #include <com/sun/star/frame/status/Visibility.hpp> 47 #include <comphelper/processfactory.hxx> 48 #include <comphelper/sequence.hxx> 49 #include <vos/mutex.hxx> 50 #include <uno/current_context.hxx> 51 #include <vcl/svapp.hxx> 52 53 #include <sfx2/app.hxx> 54 #include <sfx2/unoctitm.hxx> 55 #include <sfx2/viewfrm.hxx> 56 #include <sfx2/frame.hxx> 57 #include <sfx2/ctrlitem.hxx> 58 #include <sfx2/sfxuno.hxx> 59 #include <sfx2/bindings.hxx> 60 #include <sfx2/dispatch.hxx> 61 #include <sfx2/sfxsids.hrc> 62 #include <sfx2/request.hxx> 63 #include "statcach.hxx" 64 #include <sfx2/msgpool.hxx> 65 #include <sfx2/objsh.hxx> 66 67 namespace css = ::com::sun::star; 68 using namespace ::com::sun::star::uno; 69 using namespace ::com::sun::star::util; 70 //long nOfficeDispatchCount = 0; 71 72 enum URLTypeId 73 { 74 URLType_BOOL, 75 URLType_BYTE, 76 URLType_SHORT, 77 URLType_LONG, 78 URLType_HYPER, 79 URLType_STRING, 80 URLType_FLOAT, 81 URLType_DOUBLE, 82 URLType_COUNT 83 }; 84 85 const char* URLTypeNames[URLType_COUNT] = 86 { 87 "bool", 88 "byte", 89 "short", 90 "long", 91 "hyper", 92 "string", 93 "float", 94 "double" 95 }; 96 97 SFX_IMPL_XINTERFACE_2( SfxUnoControllerItem, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener ) 98 SFX_IMPL_XTYPEPROVIDER_2( SfxUnoControllerItem, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener ) 99 100 SfxUnoControllerItem::SfxUnoControllerItem( SfxControllerItem *pItem, SfxBindings& rBind, const String& rCmd ) 101 : pCtrlItem( pItem ) 102 , pBindings( &rBind ) 103 { 104 DBG_ASSERT( !pCtrlItem || !pCtrlItem->IsBound(), "ControllerItem fehlerhaft!" ); 105 106 aCommand.Complete = rCmd; 107 Reference < XURLTransformer > xTrans( ::comphelper::getProcessServiceFactory()->createInstance( rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer" )), UNO_QUERY ); 108 xTrans->parseStrict( aCommand ); 109 pBindings->RegisterUnoController_Impl( this ); 110 } 111 112 SfxUnoControllerItem::~SfxUnoControllerItem() 113 { 114 // tell bindings to forget this controller ( if still connected ) 115 if ( pBindings ) 116 pBindings->ReleaseUnoController_Impl( this ); 117 } 118 119 void SfxUnoControllerItem::UnBind() 120 { 121 // connection to SfxControllerItem is lost 122 pCtrlItem = NULL; 123 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY ); 124 ReleaseDispatch(); 125 } 126 127 void SAL_CALL SfxUnoControllerItem::statusChanged(const ::com::sun::star::frame::FeatureStateEvent& rEvent) throw ( ::com::sun::star::uno::RuntimeException ) 128 { 129 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 130 DBG_ASSERT( pCtrlItem, "dispatch implementation didn't respect our previous removeStatusListener call!" ); 131 132 if ( rEvent.Requery ) 133 { 134 // Fehler kann nur passieren, wenn das alte Dispatch fehlerhaft implementiert 135 // ist, also removeStatusListener nicht gefunzt hat. Aber sowas soll 136 // ja vorkommen ... 137 // Also besser vor ReleaseDispatch gegen Abflug sch"utzen! 138 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY ); 139 ReleaseDispatch(); 140 if ( pCtrlItem ) 141 GetNewDispatch(); // asynchron ?? 142 } 143 else if ( pCtrlItem ) 144 { 145 SfxItemState eState = SFX_ITEM_DISABLED; 146 SfxPoolItem* pItem = NULL; 147 if ( rEvent.IsEnabled ) 148 { 149 eState = SFX_ITEM_AVAILABLE; 150 ::com::sun::star::uno::Type pType = rEvent.State.getValueType(); 151 152 if ( pType == ::getBooleanCppuType() ) 153 { 154 sal_Bool bTemp = false; 155 rEvent.State >>= bTemp ; 156 pItem = new SfxBoolItem( pCtrlItem->GetId(), bTemp ); 157 } 158 else if ( pType == ::getCppuType((const sal_uInt16*)0) ) 159 { 160 sal_uInt16 nTemp = 0; 161 rEvent.State >>= nTemp ; 162 pItem = new SfxUInt16Item( pCtrlItem->GetId(), nTemp ); 163 } 164 else if ( pType == ::getCppuType((const sal_uInt32*)0) ) 165 { 166 sal_uInt32 nTemp = 0; 167 rEvent.State >>= nTemp ; 168 pItem = new SfxUInt32Item( pCtrlItem->GetId(), nTemp ); 169 } 170 else if ( pType == ::getCppuType((const ::rtl::OUString*)0) ) 171 { 172 ::rtl::OUString sTemp ; 173 rEvent.State >>= sTemp ; 174 pItem = new SfxStringItem( pCtrlItem->GetId(), sTemp ); 175 } 176 else 177 pItem = new SfxVoidItem( pCtrlItem->GetId() ); 178 } 179 180 pCtrlItem->StateChanged( pCtrlItem->GetId(), eState, pItem ); 181 delete pItem; 182 } 183 } 184 185 void SAL_CALL SfxUnoControllerItem::disposing( const ::com::sun::star::lang::EventObject& ) throw ( ::com::sun::star::uno::RuntimeException ) 186 { 187 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY ); 188 ReleaseDispatch(); 189 } 190 191 void SfxUnoControllerItem::ReleaseDispatch() 192 { 193 if ( xDispatch.is() ) 194 { 195 xDispatch->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aCommand ); 196 xDispatch = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); 197 } 198 } 199 200 void SfxUnoControllerItem::GetNewDispatch() 201 { 202 if ( !pBindings ) 203 { 204 // Bindings released 205 DBG_ERROR( "Tried to get dispatch, but no Bindings!" ); 206 return; 207 } 208 209 // forget old dispatch 210 xDispatch = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); 211 212 // no arms, no cookies ! 213 if ( !pBindings->GetDispatcher_Impl() || !pBindings->GetDispatcher_Impl()->GetFrame() ) 214 return; 215 216 SfxFrame& rFrame = pBindings->GetDispatcher_Impl()->GetFrame()->GetFrame(); 217 SfxFrame *pParent = rFrame.GetParentFrame(); 218 if ( pParent ) 219 // parent may intercept 220 xDispatch = TryGetDispatch( pParent ); 221 222 if ( !xDispatch.is() ) 223 { 224 // no interception 225 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame = rFrame.GetFrameInterface(); 226 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > xProv( xFrame, ::com::sun::star::uno::UNO_QUERY ); 227 if ( xProv.is() ) 228 xDispatch = xProv->queryDispatch( aCommand, ::rtl::OUString(), 0 ); 229 } 230 231 if ( xDispatch.is() ) 232 xDispatch->addStatusListener( (::com::sun::star::frame::XStatusListener*) this, aCommand ); 233 else if ( pCtrlItem ) 234 pCtrlItem->StateChanged( pCtrlItem->GetId(), SFX_ITEM_DISABLED, NULL ); 235 } 236 237 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxUnoControllerItem::TryGetDispatch( SfxFrame *pFrame ) 238 { 239 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp; 240 SfxFrame *pParent = pFrame->GetParentFrame(); 241 if ( pParent ) 242 // parent may intercept 243 xDisp = TryGetDispatch( pParent ); 244 245 // only components may intercept 246 if ( !xDisp.is() && pFrame->HasComponent() ) 247 { 248 // no interception 249 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame = pFrame->GetFrameInterface(); 250 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > xProv( xFrame, ::com::sun::star::uno::UNO_QUERY ); 251 if ( xProv.is() ) 252 xDisp = xProv->queryDispatch( aCommand, ::rtl::OUString(), 0 ); 253 } 254 255 return xDisp; 256 } 257 258 void SfxUnoControllerItem::Execute() 259 { 260 // dispatch the resource 261 ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > aSeq(1); 262 aSeq[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Referer") ); 263 aSeq[0].Value <<= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("private:select") ); 264 if ( xDispatch.is() ) 265 xDispatch->dispatch( aCommand, aSeq ); 266 } 267 268 void SfxUnoControllerItem::ReleaseBindings() 269 { 270 // connection to binding is lost; so forget the binding and the dispatch 271 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > aRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY ); 272 ReleaseDispatch(); 273 if ( pBindings ) 274 pBindings->ReleaseUnoController_Impl( this ); 275 pBindings = NULL; 276 } 277 278 void SfxStatusDispatcher::ReleaseAll() 279 { 280 ::com::sun::star::lang::EventObject aObject; 281 aObject.Source = (::cppu::OWeakObject*) this; 282 aListeners.disposeAndClear( aObject ); 283 } 284 285 void SAL_CALL SfxStatusDispatcher::dispatch( const ::com::sun::star::util::URL&, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& ) throw ( ::com::sun::star::uno::RuntimeException ) 286 { 287 } 288 289 void SAL_CALL SfxStatusDispatcher::dispatchWithNotification( 290 const ::com::sun::star::util::URL&, 291 const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >&, 292 const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& ) throw( ::com::sun::star::uno::RuntimeException ) 293 { 294 } 295 296 SFX_IMPL_XINTERFACE_2( SfxStatusDispatcher, OWeakObject, ::com::sun::star::frame::XNotifyingDispatch, ::com::sun::star::frame::XDispatch ) 297 SFX_IMPL_XTYPEPROVIDER_2( SfxStatusDispatcher, ::com::sun::star::frame::XNotifyingDispatch, ::com::sun::star::frame::XDispatch ) 298 //IMPLNAME "com.sun.star.comp.sfx2.StatusDispatcher", 299 300 SfxStatusDispatcher::SfxStatusDispatcher() 301 : aListeners( aMutex ) 302 { 303 } 304 305 void SAL_CALL SfxStatusDispatcher::addStatusListener(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL) throw ( ::com::sun::star::uno::RuntimeException ) 306 { 307 aListeners.addInterface( aURL.Complete, aListener ); 308 if ( aURL.Complete.compareToAscii(".uno:LifeTime")==0 ) 309 { 310 ::com::sun::star::frame::FeatureStateEvent aEvent; 311 aEvent.FeatureURL = aURL; 312 aEvent.Source = (::com::sun::star::frame::XDispatch*) this; 313 aEvent.IsEnabled = sal_True; 314 aEvent.Requery = sal_False; 315 aListener->statusChanged( aEvent ); 316 } 317 } 318 319 void SAL_CALL SfxStatusDispatcher::removeStatusListener( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL ) throw ( ::com::sun::star::uno::RuntimeException ) 320 { 321 aListeners.removeInterface( aURL.Complete, aListener ); 322 } 323 324 SFX_IMPL_XINTERFACE_1( SfxOfficeDispatch, SfxStatusDispatcher, ::com::sun::star::lang::XUnoTunnel ) 325 SFX_IMPL_XTYPEPROVIDER_2( SfxOfficeDispatch, ::com::sun::star::frame::XNotifyingDispatch, ::com::sun::star::lang::XUnoTunnel ) 326 327 328 //------------------------------------------------------------------------- 329 // XUnoTunnel 330 sal_Int64 SAL_CALL SfxOfficeDispatch::getSomething( const ::com::sun::star::uno::Sequence< sal_Int8 >& aIdentifier ) throw(::com::sun::star::uno::RuntimeException) 331 { 332 if ( aIdentifier == impl_getStaticIdentifier() ) 333 return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this )); 334 else 335 return 0; 336 } 337 338 /* ASDBG 339 void* SfxOfficeDispatch::getImplementation(Reflection *p) 340 { 341 if( p == ::getCppuType((const SfxOfficeDispatch*)0) ) 342 return this; 343 else 344 return ::cppu::OWeakObject::getImplementation(p); 345 346 } 347 348 Reflection* ::getCppuType((const SfxOfficeDispatch*)0) 349 { 350 static StandardClassReflection aRefl( 351 0, 352 createStandardClass( 353 "SfxOfficeDispatch", ::cppu::OWeakObject::get::cppu::OWeakObjectIdlClass(), 354 1, 355 ::getCppuType((const ::com::sun::star::frame::XDispatch*)0) ) ); 356 return &aRefl; 357 } 358 */ 359 360 SfxOfficeDispatch::SfxOfficeDispatch( SfxBindings& rBindings, SfxDispatcher* pDispat, const SfxSlot* pSlot, const ::com::sun::star::util::URL& rURL ) 361 { 362 // nOfficeDispatchCount++; 363 364 // this object is an adapter that shows a ::com::sun::star::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state 365 pControllerItem = new SfxDispatchController_Impl( this, &rBindings, pDispat, pSlot, rURL ); 366 } 367 368 SfxOfficeDispatch::SfxOfficeDispatch( SfxDispatcher* pDispat, const SfxSlot* pSlot, const ::com::sun::star::util::URL& rURL ) 369 { 370 // nOfficeDispatchCount++; 371 372 // this object is an adapter that shows a ::com::sun::star::frame::XDispatch-Interface to the outside and uses a SfxControllerItem to monitor a state 373 pControllerItem = new SfxDispatchController_Impl( this, NULL, pDispat, pSlot, rURL ); 374 } 375 376 SfxOfficeDispatch::~SfxOfficeDispatch() 377 { 378 // --nOfficeDispatchCount; 379 380 if ( pControllerItem ) 381 { 382 // when dispatch object is released, destroy its connection to this object and destroy it 383 pControllerItem->UnBindController(); 384 delete pControllerItem; 385 } 386 } 387 388 const ::com::sun::star::uno::Sequence< sal_Int8 >& SfxOfficeDispatch::impl_getStaticIdentifier() 389 { 390 // {38 57 CA 80 09 36 11 d4 83 FE 00 50 04 52 6B 21} 391 static sal_uInt8 pGUID[16] = { 0x38, 0x57, 0xCA, 0x80, 0x09, 0x36, 0x11, 0xd4, 0x83, 0xFE, 0x00, 0x50, 0x04, 0x52, 0x6B, 0x21 }; 392 static ::com::sun::star::uno::Sequence< sal_Int8 > seqID((sal_Int8*)pGUID,16) ; 393 return seqID ; 394 } 395 396 397 void SAL_CALL SfxOfficeDispatch::dispatch( const ::com::sun::star::util::URL& aURL, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs ) throw ( ::com::sun::star::uno::RuntimeException ) 398 { 399 // ControllerItem is the Impl class 400 if ( pControllerItem ) 401 { 402 // The JavaContext contains an interaction handler which is used when 403 // the creation of a Java Virtual Machine fails. The second parameter 404 // indicates, that there shall only be one user notification (message box) 405 // even if the same error (interaction) reoccurs. The effect is, that if a 406 // user selects a menu entry than they may get only one notification that 407 // a JRE is not selected. 408 com::sun::star::uno::ContextLayer layer( 409 new svt::JavaContext( com::sun::star::uno::getCurrentContext(), 410 true) ); 411 412 pControllerItem->dispatch( aURL, aArgs, ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchResultListener >() ); 413 } 414 } 415 416 void SAL_CALL SfxOfficeDispatch::dispatchWithNotification( const ::com::sun::star::util::URL& aURL, 417 const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs, 418 const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& rListener ) throw( ::com::sun::star::uno::RuntimeException ) 419 { 420 // ControllerItem is the Impl class 421 if ( pControllerItem ) 422 { 423 // see comment for SfxOfficeDispatch::dispatch 424 com::sun::star::uno::ContextLayer layer( 425 new svt::JavaContext( com::sun::star::uno::getCurrentContext(), 426 true) ); 427 428 pControllerItem->dispatch( aURL, aArgs, rListener ); 429 } 430 } 431 432 void SAL_CALL SfxOfficeDispatch::addStatusListener(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL) throw ( ::com::sun::star::uno::RuntimeException ) 433 { 434 GetListeners().addInterface( aURL.Complete, aListener ); 435 if ( pControllerItem ) 436 { 437 // ControllerItem is the Impl class 438 pControllerItem->addStatusListener( aListener, aURL ); 439 } 440 } 441 442 SfxDispatcher* SfxOfficeDispatch::GetDispatcher_Impl() 443 { 444 return pControllerItem->GetDispatcher(); 445 } 446 447 void SfxOfficeDispatch::SetFrame(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame) 448 { 449 if ( pControllerItem ) 450 pControllerItem->SetFrame( xFrame ); 451 } 452 453 void SfxOfficeDispatch::SetMasterUnoCommand( sal_Bool bSet ) 454 { 455 if ( pControllerItem ) 456 pControllerItem->setMasterSlaveCommand( bSet ); 457 } 458 459 sal_Bool SfxOfficeDispatch::IsMasterUnoCommand() const 460 { 461 if ( pControllerItem ) 462 return pControllerItem->isMasterSlaveCommand(); 463 return sal_False; 464 } 465 466 // Determine if URL contains a master/slave command which must be handled a little bit different 467 sal_Bool SfxOfficeDispatch::IsMasterUnoCommand( const ::com::sun::star::util::URL& aURL ) 468 { 469 if ( aURL.Protocol.equalsAscii( ".uno:" ) && 470 ( aURL.Path.indexOf( '.' ) > 0 )) 471 return sal_True; 472 473 return sal_False; 474 } 475 476 rtl::OUString SfxOfficeDispatch::GetMasterUnoCommand( const ::com::sun::star::util::URL& aURL ) 477 { 478 rtl::OUString aMasterCommand; 479 if ( IsMasterUnoCommand( aURL )) 480 { 481 sal_Int32 nIndex = aURL.Path.indexOf( '.' ); 482 if ( nIndex > 0 ) 483 aMasterCommand = aURL.Path.copy( 0, nIndex ); 484 } 485 486 return aMasterCommand; 487 } 488 489 SfxDispatchController_Impl::SfxDispatchController_Impl( 490 SfxOfficeDispatch* pDisp, 491 SfxBindings* pBind, 492 SfxDispatcher* pDispat, 493 const SfxSlot* pSlot, 494 const ::com::sun::star::util::URL& rURL ) 495 : aDispatchURL( rURL ) 496 , pDispatcher( pDispat ) 497 , pBindings( pBind ) 498 , pLastState( 0 ) 499 , nSlot( pSlot->GetSlotId() ) 500 , pDispatch( pDisp ) 501 , bMasterSlave( sal_False ) 502 , bVisible( sal_True ) 503 , pUnoName( pSlot->pUnoName ) 504 { 505 if ( aDispatchURL.Protocol.equalsAscii("slot:") && pUnoName ) 506 { 507 ByteString aTmp(".uno:"); 508 aTmp += pUnoName; 509 aDispatchURL.Complete = ::rtl::OUString::createFromAscii( aTmp.GetBuffer() ); 510 Reference < ::com::sun::star::util::XURLTransformer > xTrans( ::comphelper::getProcessServiceFactory()->createInstance( rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer" )), UNO_QUERY ); 511 xTrans->parseStrict( aDispatchURL ); 512 } 513 514 SetId( nSlot ); 515 if ( pBindings ) 516 { 517 // Bind immediately to enable the cache to recycle dispatches when asked for the same command 518 // a command in "slot" or in ".uno" notation must be treated as identical commands! 519 pBindings->ENTERREGISTRATIONS(); 520 BindInternal_Impl( nSlot, pBindings ); 521 pBindings->LEAVEREGISTRATIONS(); 522 } 523 } 524 525 SfxDispatchController_Impl::~SfxDispatchController_Impl() 526 { 527 if ( pLastState && !IsInvalidItem( pLastState ) ) 528 delete pLastState; 529 530 if ( pDispatch ) 531 { 532 // disconnect 533 pDispatch->pControllerItem = NULL; 534 535 // force all listeners to release the dispatch object 536 ::com::sun::star::lang::EventObject aObject; 537 aObject.Source = (::cppu::OWeakObject*) pDispatch; 538 pDispatch->GetListeners().disposeAndClear( aObject ); 539 } 540 } 541 542 void SfxDispatchController_Impl::SetFrame(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& _xFrame) 543 { 544 xFrame = _xFrame; 545 } 546 547 void SfxDispatchController_Impl::setMasterSlaveCommand( sal_Bool bSet ) 548 { 549 bMasterSlave = bSet; 550 } 551 552 sal_Bool SfxDispatchController_Impl::isMasterSlaveCommand() const 553 { 554 return bMasterSlave; 555 } 556 557 void SfxDispatchController_Impl::UnBindController() 558 { 559 pDispatch = NULL; 560 if ( IsBound() ) 561 { 562 GetBindings().ENTERREGISTRATIONS(); 563 SfxControllerItem::UnBind(); 564 GetBindings().LEAVEREGISTRATIONS(); 565 } 566 } 567 568 void SfxDispatchController_Impl::addParametersToArgs( const com::sun::star::util::URL& aURL, ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rArgs ) const 569 { 570 // Extract the parameter from the URL and put them into the property value sequence 571 sal_Int32 nQueryIndex = aURL.Complete.indexOf( '?' ); 572 if ( nQueryIndex > 0 ) 573 { 574 rtl::OUString aParamString( aURL.Complete.copy( nQueryIndex+1 )); 575 sal_Int32 nIndex = 0; 576 do 577 { 578 rtl::OUString aToken = aParamString.getToken( 0, '&', nIndex ); 579 580 sal_Int32 nParmIndex = 0; 581 rtl::OUString aParamType; 582 rtl::OUString aParamName = aToken.getToken( 0, '=', nParmIndex ); 583 rtl::OUString aValue = (nParmIndex!=-1) ? aToken.getToken( 0, '=', nParmIndex ) : ::rtl::OUString(); 584 585 if ( aParamName.getLength() > 0 ) 586 { 587 nParmIndex = 0; 588 aToken = aParamName; 589 aParamName = (nParmIndex!=-1) ? aToken.getToken( 0, ':', nParmIndex ) : ::rtl::OUString(); 590 aParamType = (nParmIndex!=-1) ? aToken.getToken( 0, ':', nParmIndex ) : ::rtl::OUString(); 591 } 592 593 sal_Int32 nLen = rArgs.getLength(); 594 rArgs.realloc( nLen+1 ); 595 rArgs[nLen].Name = aParamName; 596 597 if ( aParamType.getLength() == 0 ) 598 { 599 // Default: LONG 600 rArgs[nLen].Value <<= aValue.toInt32(); 601 } 602 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BOOL], 4 )) 603 { 604 // sal_Bool support 605 rArgs[nLen].Value <<= aValue.toBoolean(); 606 } 607 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_BYTE], 4 )) 608 { 609 // sal_uInt8 support 610 rArgs[nLen].Value <<= sal_Int8( aValue.toInt32() ); 611 } 612 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_LONG], 4 )) 613 { 614 // LONG support 615 rArgs[nLen].Value <<= aValue.toInt32(); 616 } 617 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_SHORT], 5 )) 618 { 619 // SHORT support 620 rArgs[nLen].Value <<= sal_Int8( aValue.toInt32() ); 621 } 622 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_HYPER], 5 )) 623 { 624 // HYPER support 625 rArgs[nLen].Value <<= aValue.toInt64(); 626 } 627 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_FLOAT], 5 )) 628 { 629 // FLOAT support 630 rArgs[nLen].Value <<= aValue.toFloat(); 631 } 632 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_STRING], 6 )) 633 { 634 // STRING support 635 rArgs[nLen].Value <<= rtl::OUString( INetURLObject::decode( aValue, '%', INetURLObject::DECODE_WITH_CHARSET )); 636 } 637 else if ( aParamType.equalsAsciiL( URLTypeNames[URLType_DOUBLE], 6)) 638 { 639 // DOUBLE support 640 rArgs[nLen].Value <<= aValue.toDouble(); 641 } 642 } 643 while ( nIndex >= 0 ); 644 } 645 } 646 647 SfxMapUnit SfxDispatchController_Impl::GetCoreMetric( SfxItemPool& rPool, sal_uInt16 nSlotId ) 648 { 649 sal_uInt16 nWhich = rPool.GetWhich( nSlotId ); 650 return rPool.GetMetric( nWhich ); 651 } 652 653 rtl::OUString SfxDispatchController_Impl::getSlaveCommand( const ::com::sun::star::util::URL& rURL ) 654 { 655 rtl::OUString aSlaveCommand; 656 sal_Int32 nIndex = rURL.Path.indexOf( '.' ); 657 if (( nIndex > 0 ) && ( nIndex < rURL.Path.getLength() )) 658 aSlaveCommand = rURL.Path.copy( nIndex+1 ); 659 return aSlaveCommand; 660 } 661 662 void SAL_CALL SfxDispatchController_Impl::dispatch( const ::com::sun::star::util::URL& aURL, 663 const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& aArgs, 664 const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchResultListener >& rListener ) throw( ::com::sun::star::uno::RuntimeException ) 665 { 666 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 667 if ( 668 pDispatch && 669 ( 670 (aURL.Protocol.equalsAsciiL( ".uno:", 5 ) && aURL.Path == aDispatchURL.Path) || 671 (aURL.Protocol.equalsAsciiL( "slot:", 5 ) && aURL.Path.toInt32() == GetId()) 672 ) 673 ) 674 { 675 /* 676 if ( !IsBound() && pBindings ) 677 { 678 pBindings->ENTERREGISTRATIONS(); 679 BindInternal_Impl( nSlot, pBindings ); 680 pBindings->LEAVEREGISTRATIONS(); 681 } */ 682 683 if ( !pDispatcher && pBindings ) 684 pDispatcher = GetBindings().GetDispatcher_Impl(); 685 686 ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > lNewArgs; 687 sal_Int32 nCount = aArgs.getLength(); 688 689 // Support for URL based arguments 690 INetURLObject aURLObj( aURL.Complete ); 691 if ( aURLObj.HasParam() ) 692 addParametersToArgs( aURL, lNewArgs ); 693 694 // Try to find call mode and frame name inside given arguments... 695 SfxCallMode nCall = SFX_CALLMODE_STANDARD; 696 sal_Int32 nMarkArg = -1; 697 698 // Filter arguments which shouldn't be part of the sequence property value 699 sal_Bool bTemp = sal_Bool(); 700 sal_uInt16 nModifier(0); 701 std::vector< ::com::sun::star::beans::PropertyValue > aAddArgs; 702 for( sal_Int32 n=0; n<nCount; n++ ) 703 { 704 const ::com::sun::star::beans::PropertyValue& rProp = aArgs[n]; 705 if( rProp.Name.equalsAsciiL("SynchronMode",12)) 706 { 707 if( rProp.Value >>=bTemp ) 708 nCall = bTemp ? SFX_CALLMODE_SYNCHRON : SFX_CALLMODE_ASYNCHRON; 709 } 710 else if( rProp.Name.equalsAsciiL("Bookmark",8)) 711 { 712 nMarkArg = n; 713 aAddArgs.push_back( aArgs[n] ); 714 } 715 else if( rProp.Name.equalsAsciiL("KeyModifier",11)) 716 rProp.Value >>= nModifier; 717 else 718 aAddArgs.push_back( aArgs[n] ); 719 } 720 721 // Add needed arguments to sequence property value 722 sal_uInt32 nAddArgs = aAddArgs.size(); 723 if ( nAddArgs > 0 ) 724 { 725 sal_uInt32 nIndex( lNewArgs.getLength() ); 726 727 lNewArgs.realloc( lNewArgs.getLength()+aAddArgs.size() ); 728 for ( sal_uInt32 i = 0; i < nAddArgs; i++ ) 729 lNewArgs[nIndex++] = aAddArgs[i]; 730 } 731 732 // Overwrite possible detected sychron argument, if real listener exists (currently no other way) 733 if ( rListener.is() ) 734 nCall = SFX_CALLMODE_SYNCHRON; 735 736 if( GetId() == SID_JUMPTOMARK && nMarkArg == - 1 ) 737 { 738 // we offer dispatches for SID_JUMPTOMARK if the URL points to a bookmark inside the document 739 // so we must retrieve this as an argument from the parsed URL 740 lNewArgs.realloc( lNewArgs.getLength()+1 ); 741 nMarkArg = lNewArgs.getLength()-1; 742 lNewArgs[nMarkArg].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Bookmark")); 743 lNewArgs[nMarkArg].Value <<= aURL.Mark; 744 } 745 746 css::uno::Reference< css::frame::XFrame > xFrameRef(xFrame.get(), css::uno::UNO_QUERY); 747 if (! xFrameRef.is() && pDispatcher) 748 { 749 SfxViewFrame* pViewFrame = pDispatcher->GetFrame(); 750 if (pViewFrame) 751 xFrameRef = pViewFrame->GetFrame().GetFrameInterface(); 752 } 753 SfxAllItemSet aInternalSet( SFX_APP()->GetPool() ); 754 if (xFrameRef.is()) // an empty set is no problem ... but an empty frame reference can be a problem ! 755 aInternalSet.Put( SfxUnoFrameItem( SID_FILLFRAME, xFrameRef ) ); 756 757 sal_Bool bSuccess = sal_False; 758 sal_Bool bFailure = sal_False; 759 const SfxPoolItem* pItem = NULL; 760 SfxShell* pShell( 0 ); 761 // #i102619# Retrieve metric from shell before execution - the shell could be destroyed after execution 762 SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM ); 763 if ( pDispatcher->GetBindings() ) 764 { 765 if ( !pDispatcher->IsLocked( GetId() ) ) 766 { 767 const SfxSlot *pSlot = 0; 768 if ( pDispatcher->GetShellAndSlot_Impl( GetId(), &pShell, &pSlot, sal_False, 769 SFX_CALLMODE_MODAL==(nCall&SFX_CALLMODE_MODAL), sal_False ) ) 770 { 771 if ( bMasterSlave ) 772 { 773 // Extract slave command and add argument to the args list. Master slot MUST 774 // have a argument that has the same name as the master slot and type is SfxStringItem. 775 sal_Int32 nIndex = lNewArgs.getLength(); 776 lNewArgs.realloc( nIndex+1 ); 777 lNewArgs[nIndex].Name = rtl::OUString::createFromAscii( pSlot->pUnoName ); 778 lNewArgs[nIndex].Value = makeAny( SfxDispatchController_Impl::getSlaveCommand( aDispatchURL )); 779 } 780 781 eMapUnit = GetCoreMetric( pShell->GetPool(), GetId() ); 782 SfxAllItemSet aSet( pShell->GetPool() ); 783 TransformParameters( GetId(), lNewArgs, aSet, pSlot ); 784 if ( aSet.Count() ) 785 { 786 // execute with arguments - call directly 787 pItem = pDispatcher->Execute( GetId(), nCall, &aSet, &aInternalSet, nModifier ); 788 bSuccess = (pItem != NULL); 789 } 790 else 791 { 792 // execute using bindings, enables support for toggle/enum etc. 793 SfxRequest aReq( GetId(), nCall, pShell->GetPool() ); 794 aReq.SetModifier( nModifier ); 795 aReq.SetInternalArgs_Impl(aInternalSet); 796 pDispatcher->GetBindings()->Execute_Impl( aReq, pSlot, pShell ); 797 pItem = aReq.GetReturnValue(); 798 bSuccess = aReq.IsDone() || pItem != NULL; 799 bFailure = aReq.IsCancelled(); 800 } 801 } 802 #ifdef DBG_UTIL 803 else 804 DBG_WARNING("MacroPlayer: Unknown slot dispatched!"); 805 #endif 806 } 807 } 808 else 809 { 810 eMapUnit = GetCoreMetric( SFX_APP()->GetPool(), GetId() ); 811 // AppDispatcher 812 SfxAllItemSet aSet( SFX_APP()->GetPool() ); 813 TransformParameters( GetId(), lNewArgs, aSet ); 814 815 if ( aSet.Count() ) 816 pItem = pDispatcher->Execute( GetId(), nCall, &aSet, &aInternalSet, nModifier ); 817 else 818 // SfxRequests take empty sets as argument sets, GetArgs() returning non-zero! 819 pItem = pDispatcher->Execute( GetId(), nCall, 0, &aInternalSet, nModifier ); 820 821 // no bindings, no invalidate ( usually done in SfxDispatcher::Call_Impl()! ) 822 if ( SfxApplication::Get() ) 823 { 824 SfxDispatcher* pAppDispat = SFX_APP()->GetAppDispatcher_Impl(); 825 if ( pAppDispat ) 826 { 827 const SfxPoolItem* pState=0; 828 SfxItemState eState = pDispatcher->QueryState( GetId(), pState ); 829 StateChanged( GetId(), eState, pState ); 830 } 831 } 832 833 bSuccess = (pItem != NULL); 834 } 835 836 if ( rListener.is() ) 837 { 838 ::com::sun::star::frame::DispatchResultEvent aEvent; 839 if ( bSuccess ) 840 aEvent.State = com::sun::star::frame::DispatchResultState::SUCCESS; 841 // else if ( bFailure ) 842 else 843 aEvent.State = com::sun::star::frame::DispatchResultState::FAILURE; 844 // else 845 // aEvent.State = com::sun::star::frame::DispatchResultState::DONTKNOW; 846 847 aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch; 848 if ( bSuccess && pItem && !pItem->ISA(SfxVoidItem) ) 849 { 850 sal_uInt16 nSubId( 0 ); 851 if ( eMapUnit == SFX_MAPUNIT_TWIP ) 852 nSubId |= CONVERT_TWIPS; 853 pItem->QueryValue( aEvent.Result, (sal_uInt8)nSubId ); 854 } 855 856 rListener->dispatchFinished( aEvent ); 857 } 858 } 859 } 860 861 SfxDispatcher* SfxDispatchController_Impl::GetDispatcher() 862 { 863 if ( !pDispatcher && pBindings ) 864 pDispatcher = GetBindings().GetDispatcher_Impl(); 865 return pDispatcher; 866 } 867 868 void SAL_CALL SfxDispatchController_Impl::addStatusListener(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > & aListener, const ::com::sun::star::util::URL& aURL) throw ( ::com::sun::star::uno::RuntimeException ) 869 { 870 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 871 if ( !pDispatch ) 872 return; 873 874 /*if ( !IsBound() && pBindings ) 875 { 876 pBindings->ENTERREGISTRATIONS(); 877 BindInternal_Impl( nSlot, pBindings ); 878 pBindings->LEAVEREGISTRATIONS(); 879 } */ 880 881 // Use alternative QueryState call to have a valid UNO representation of the state. 882 ::com::sun::star::uno::Any aState; 883 if ( !pDispatcher && pBindings ) 884 pDispatcher = GetBindings().GetDispatcher_Impl(); 885 SfxItemState eState = pDispatcher->QueryState( GetId(), aState ); 886 887 if ( eState == SFX_ITEM_DONTCARE ) 888 { 889 // Use special uno struct to transport don't care state 890 ::com::sun::star::frame::status::ItemStatus aItemStatus; 891 aItemStatus.State = ::com::sun::star::frame::status::ItemState::dont_care; 892 aState = makeAny( aItemStatus ); 893 } 894 895 ::com::sun::star::frame::FeatureStateEvent aEvent; 896 aEvent.FeatureURL = aURL; 897 aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch; 898 aEvent.Requery = sal_False; 899 if ( bVisible ) 900 { 901 aEvent.IsEnabled = eState != SFX_ITEM_DISABLED; 902 aEvent.State = aState; 903 } 904 else 905 { 906 ::com::sun::star::frame::status::Visibility aVisibilityStatus; 907 aVisibilityStatus.bVisible = sal_False; 908 909 // MBA: we might decide to *not* disable "invisible" slots, but this would be 910 // a change that needs to adjust at least the testtool 911 aEvent.IsEnabled = sal_False; 912 aEvent.State = makeAny( aVisibilityStatus ); 913 } 914 915 aListener->statusChanged( aEvent ); 916 } 917 918 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState, SfxSlotServer* pSlotServ ) 919 { 920 if ( !pDispatch ) 921 return; 922 923 // Bindings instance notifies controller about a state change, listeners must be notified also 924 // Don't cache visibility state changes as they are volatile. We need our real state to send it 925 // to our controllers after visibility is set to true. 926 sal_Bool bNotify = sal_True; 927 if ( pState && !IsInvalidItem( pState ) ) 928 { 929 if ( !pState->ISA( SfxVisibilityItem ) ) 930 { 931 sal_Bool bBothAvailable = pLastState && !IsInvalidItem(pLastState); 932 if ( bBothAvailable ) 933 bNotify = pState->Type() != pLastState->Type() || *pState != *pLastState; 934 if ( pLastState && !IsInvalidItem( pLastState ) ) 935 delete pLastState; 936 pLastState = !IsInvalidItem(pState) ? pState->Clone() : pState; 937 bVisible = sal_True; 938 } 939 else 940 bVisible = ((SfxVisibilityItem *)pState)->GetValue(); 941 } 942 else 943 { 944 if ( pLastState && !IsInvalidItem( pLastState ) ) 945 delete pLastState; 946 pLastState = pState; 947 } 948 949 ::cppu::OInterfaceContainerHelper* pContnr = pDispatch->GetListeners().getContainer ( aDispatchURL.Complete ); 950 if ( bNotify && pContnr ) 951 { 952 ::com::sun::star::uno::Any aState; 953 if ( ( eState >= SFX_ITEM_AVAILABLE ) && pState && !IsInvalidItem( pState ) && !pState->ISA(SfxVoidItem) ) 954 { 955 // Retrieve metric from pool to have correct sub ID when calling QueryValue 956 sal_uInt16 nSubId( 0 ); 957 SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM ); 958 959 // retrieve the core metric 960 // it's enough to check the objectshell, the only shell that does not use the pool of the document 961 // is SfxViewFrame, but it hasn't any metric parameters 962 // TODO/LATER: what about the FormShell? Does it use any metric data?! Perhaps it should use the Pool of the document! 963 if ( pSlotServ && pDispatcher ) 964 { 965 SfxShell* pShell = pDispatcher->GetShell( pSlotServ->GetShellLevel() ); 966 DBG_ASSERT( pShell, "Can't get core metric without shell!" ); 967 if ( pShell ) 968 eMapUnit = GetCoreMetric( pShell->GetPool(), nSID ); 969 } 970 971 if ( eMapUnit == SFX_MAPUNIT_TWIP ) 972 nSubId |= CONVERT_TWIPS; 973 974 pState->QueryValue( aState, (sal_uInt8)nSubId ); 975 } 976 else if ( eState == SFX_ITEM_DONTCARE ) 977 { 978 // Use special uno struct to transport don't care state 979 ::com::sun::star::frame::status::ItemStatus aItemStatus; 980 aItemStatus.State = ::com::sun::star::frame::status::ItemState::dont_care; 981 aState = makeAny( aItemStatus ); 982 } 983 984 ::com::sun::star::frame::FeatureStateEvent aEvent; 985 aEvent.FeatureURL = aDispatchURL; 986 aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch; 987 aEvent.IsEnabled = eState != SFX_ITEM_DISABLED; 988 aEvent.Requery = sal_False; 989 aEvent.State = aState; 990 991 ::cppu::OInterfaceIteratorHelper aIt( *pContnr ); 992 while( aIt.hasMoreElements() ) 993 { 994 try 995 { 996 ((::com::sun::star::frame::XStatusListener *)aIt.next())->statusChanged( aEvent ); 997 } 998 catch( ::com::sun::star::uno::RuntimeException& ) 999 { 1000 aIt.remove(); 1001 } 1002 } 1003 } 1004 } 1005 1006 void SfxDispatchController_Impl::StateChanged( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) 1007 { 1008 StateChanged( nSID, eState, pState, 0 ); 1009 } 1010