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_sfx2.hxx" 30 31 #ifdef SOLARIS 32 // HACK: prevent conflict between STLPORT and Workshop headers on Solaris 8 33 #include <ctime> 34 #endif 35 36 #include <string> // HACK: prevent conflict between STLPORT and Workshop headers 37 #include <com/sun/star/util/XURLTransformer.hpp> 38 #include <com/sun/star/frame/XController.hpp> 39 #include <com/sun/star/frame/XFrameActionListener.hpp> 40 #include <com/sun/star/frame/XComponentLoader.hpp> 41 #include <com/sun/star/frame/XFrame.hpp> 42 #include <com/sun/star/frame/FrameActionEvent.hpp> 43 #include <com/sun/star/frame/FrameAction.hpp> 44 #include <com/sun/star/beans/PropertyValue.hpp> 45 #include <cppuhelper/weak.hxx> 46 #include <svl/eitem.hxx> 47 #include <svl/intitem.hxx> 48 #include <svl/stritem.hxx> 49 #include <svl/visitem.hxx> 50 #include <comphelper/processfactory.hxx> 51 52 #ifndef GCC 53 #endif 54 55 #include <sfx2/app.hxx> 56 #include <sfx2/appuno.hxx> 57 #include "statcach.hxx" 58 #include <sfx2/msg.hxx> 59 #include <sfx2/ctrlitem.hxx> 60 #include <sfx2/dispatch.hxx> 61 #include "sfxtypes.hxx" 62 #include <sfx2/sfxuno.hxx> 63 #include <sfx2/unoctitm.hxx> 64 #include <sfx2/msgpool.hxx> 65 #include <sfx2/viewfrm.hxx> 66 67 using namespace ::com::sun::star; 68 using namespace ::com::sun::star::uno; 69 using namespace ::com::sun::star::util; 70 71 //==================================================================== 72 73 DBG_NAME(SfxStateCache) 74 DBG_NAME(SfxStateCacheSetState) 75 76 SFX_IMPL_XINTERFACE_2( BindDispatch_Impl, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener ) 77 SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener ) 78 79 //----------------------------------------------------------------------------- 80 BindDispatch_Impl::BindDispatch_Impl( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > & rDisp, const ::com::sun::star::util::URL& rURL, SfxStateCache *pStateCache, const SfxSlot* pS ) 81 : xDisp( rDisp ) 82 , aURL( rURL ) 83 , pCache( pStateCache ) 84 , pSlot( pS ) 85 { 86 DBG_ASSERT( pCache && pSlot, "Invalid BindDispatch!"); 87 aStatus.IsEnabled = sal_True; 88 } 89 90 void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& ) throw( ::com::sun::star::uno::RuntimeException ) 91 { 92 if ( xDisp.is() ) 93 { 94 xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL ); 95 xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); 96 } 97 } 98 99 void SAL_CALL BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException ) 100 { 101 aStatus = rEvent; 102 if ( !pCache ) 103 return; 104 105 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener > xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY ); 106 if ( aStatus.Requery ) 107 pCache->Invalidate( sal_True ); 108 else 109 { 110 SfxPoolItem *pItem=NULL; 111 sal_uInt16 nId = pCache->GetId(); 112 SfxItemState eState = SFX_ITEM_DISABLED; 113 // pCache->Invalidate( sal_False ); 114 if ( !aStatus.IsEnabled ) 115 { 116 // default 117 } 118 else if (aStatus.State.hasValue()) 119 { 120 eState = SFX_ITEM_AVAILABLE; 121 ::com::sun::star::uno::Any aAny = aStatus.State; 122 123 ::com::sun::star::uno::Type pType = aAny.getValueType(); 124 if ( pType == ::getBooleanCppuType() ) 125 { 126 sal_Bool bTemp = false; 127 aAny >>= bTemp ; 128 pItem = new SfxBoolItem( nId, bTemp ); 129 } 130 else if ( pType == ::getCppuType((const sal_uInt16*)0) ) 131 { 132 sal_uInt16 nTemp = 0; 133 aAny >>= nTemp ; 134 pItem = new SfxUInt16Item( nId, nTemp ); 135 } 136 else if ( pType == ::getCppuType((const sal_uInt32*)0) ) 137 { 138 sal_uInt32 nTemp = 0; 139 aAny >>= nTemp ; 140 pItem = new SfxUInt32Item( nId, nTemp ); 141 } 142 else if ( pType == ::getCppuType((const ::rtl::OUString*)0) ) 143 { 144 ::rtl::OUString sTemp ; 145 aAny >>= sTemp ; 146 pItem = new SfxStringItem( nId, sTemp ); 147 } 148 else 149 { 150 if ( pSlot ) 151 pItem = pSlot->GetType()->CreateItem(); 152 if ( pItem ) 153 { 154 pItem->SetWhich( nId ); 155 pItem->PutValue( aAny ); 156 } 157 else 158 pItem = new SfxVoidItem( nId ); 159 } 160 } 161 else 162 { 163 // DONTCARE status 164 pItem = new SfxVoidItem(0); 165 eState = SFX_ITEM_UNKNOWN; 166 } 167 168 for ( SfxControllerItem *pCtrl = pCache->GetItemLink(); 169 pCtrl; 170 pCtrl = pCtrl->GetItemLink() ) 171 pCtrl->StateChanged( nId, eState, pItem ); 172 173 delete pItem; 174 } 175 } 176 177 void BindDispatch_Impl::Release() 178 { 179 if ( xDisp.is() ) 180 { 181 xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL ); 182 xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); 183 } 184 185 pCache = NULL; 186 release(); 187 } 188 189 const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const 190 { 191 return aStatus; 192 } 193 194 void BindDispatch_Impl::Dispatch( uno::Sequence < beans::PropertyValue > aProps, sal_Bool bForceSynchron ) 195 { 196 if ( xDisp.is() && aStatus.IsEnabled ) 197 { 198 sal_Int32 nLength = aProps.getLength(); 199 aProps.realloc(nLength+1); 200 aProps[nLength].Name = DEFINE_CONST_UNICODE("SynchronMode"); 201 aProps[nLength].Value <<= bForceSynchron ; 202 xDisp->dispatch( aURL, aProps ); 203 } 204 } 205 206 //-------------------------------------------------------------------- 207 208 /* Dieser Konstruktor fuer einen ungueltigen Cache, der sich also 209 bei der ersten Anfrage zun"achst updated. 210 */ 211 212 SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ): 213 pDispatch( 0 ), 214 nId(nFuncId), 215 pInternalController(0), 216 pController(0), 217 pLastItem( 0 ), 218 eLastState( 0 ), 219 bItemVisible( sal_True ) 220 { 221 DBG_MEMTEST(); 222 DBG_CTOR(SfxStateCache, 0); 223 bCtrlDirty = sal_True; 224 bSlotDirty = sal_True; 225 bItemDirty = sal_True; 226 } 227 228 //-------------------------------------------------------------------- 229 230 /* Der Destruktor pr"uft per Assertion, ob noch Controller angemeldet 231 sind. 232 */ 233 234 SfxStateCache::~SfxStateCache() 235 { 236 DBG_MEMTEST(); 237 DBG_DTOR(SfxStateCache, 0); 238 DBG_ASSERT( pController == 0 && pInternalController == 0, "es sind noch Controller angemeldet" ); 239 if ( !IsInvalidItem(pLastItem) ) 240 delete pLastItem; 241 if ( pDispatch ) 242 { 243 pDispatch->Release(); 244 pDispatch = NULL; 245 } 246 } 247 248 //-------------------------------------------------------------------- 249 // invalidates the cache (next request will force update) 250 void SfxStateCache::Invalidate( sal_Bool bWithMsg ) 251 { 252 bCtrlDirty = sal_True; 253 if ( bWithMsg ) 254 { 255 bSlotDirty = sal_True; 256 aSlotServ.SetSlot( 0 ); 257 if ( pDispatch ) 258 { 259 pDispatch->Release(); 260 pDispatch = NULL; 261 } 262 } 263 } 264 265 //-------------------------------------------------------------------- 266 267 // gets the corresponding function from the dispatcher or the cache 268 269 const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv ) 270 { 271 DBG_MEMTEST(); 272 DBG_CHKTHIS(SfxStateCache, 0); 273 274 if ( bSlotDirty ) 275 { 276 // get the SlotServer; we need it for internal controllers anyway, but also in most cases 277 rDispat._FindServer( nId, aSlotServ, sal_False ); 278 279 DBG_ASSERT( !pDispatch, "Old Dispatch not removed!" ); 280 281 // we don't need to check the dispatch provider if we only have an internal controller 282 if ( xProv.is() ) 283 { 284 const SfxSlot* pSlot = aSlotServ.GetSlot(); 285 if ( !pSlot ) 286 // get the slot - even if it is disabled on the dispatcher 287 pSlot = SfxSlotPool::GetSlotPool( rDispat.GetFrame() ).GetSlot( nId ); 288 289 if ( !pSlot || !pSlot->pUnoName ) 290 { 291 bSlotDirty = sal_False; 292 bCtrlDirty = sal_True; 293 return aSlotServ.GetSlot()? &aSlotServ: 0; 294 } 295 296 // create the dispatch URL from the slot data 297 ::com::sun::star::util::URL aURL; 298 ::rtl::OUString aCmd = DEFINE_CONST_UNICODE(".uno:"); 299 aURL.Protocol = aCmd; 300 aURL.Path = ::rtl::OUString::createFromAscii( pSlot->GetUnoName() ); 301 aCmd += aURL.Path; 302 aURL.Complete = aCmd; 303 aURL.Main = aCmd; 304 305 // try to get a dispatch object for this command 306 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > xDisp = xProv->queryDispatch( aURL, ::rtl::OUString(), 0 ); 307 if ( xDisp.is() ) 308 { 309 // test the dispatch object if it is just a wrapper for a SfxDispatcher 310 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY ); 311 SfxOfficeDispatch* pDisp = NULL; 312 if ( xTunnel.is() ) 313 { 314 sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier()); 315 pDisp = reinterpret_cast< SfxOfficeDispatch* >(sal::static_int_cast< sal_IntPtr >( nImplementation )); 316 } 317 318 if ( pDisp ) 319 { 320 // The intercepting object is an SFX component 321 // If this dispatch object does not use the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component 322 // (intercepting by internal dispatches) 323 SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl(); 324 if ( pDispatcher == &rDispat || pDispatcher == SFX_APP()->GetAppDispatcher_Impl() ) 325 { 326 // so we can use it directly 327 bSlotDirty = sal_False; 328 bCtrlDirty = sal_True; 329 return aSlotServ.GetSlot()? &aSlotServ: 0; 330 } 331 } 332 333 // so the dispatch object isn't a SfxDispatcher wrapper or it is one, but it uses another dispatcher, but not rDispat 334 pDispatch = new BindDispatch_Impl( xDisp, aURL, this, pSlot ); 335 pDispatch->acquire(); 336 337 // flags must be set before adding StatusListener because the dispatch object will set the state 338 bSlotDirty = sal_False; 339 bCtrlDirty = sal_True; 340 xDisp->addStatusListener( pDispatch, aURL ); 341 } 342 else if ( rDispat.GetFrame() ) 343 { 344 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv( 345 rDispat.GetFrame()->GetFrame().GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY ); 346 if ( xFrameProv != xProv ) 347 return GetSlotServer( rDispat, xFrameProv ); 348 } 349 } 350 351 bSlotDirty = sal_False; 352 bCtrlDirty = sal_True; 353 } 354 355 // we *always* return a SlotServer (if there is one); but in case of an external dispatch we might not use it 356 // for the "real" (non internal) controllers 357 return aSlotServ.GetSlot()? &aSlotServ: 0; 358 } 359 360 361 //-------------------------------------------------------------------- 362 363 // Status setzen in allen Controllern 364 365 void SfxStateCache::SetState 366 ( 367 SfxItemState eState, // <SfxItemState> von 'pState' 368 const SfxPoolItem* pState, // Status des Slots, ggf. 0 oder -1 369 sal_Bool bMaybeDirty 370 ) 371 372 /* [Beschreibung] 373 374 Diese Methode verteilt die Status auf alle an dieser SID gebundenen 375 <SfxControllerItem>s. Ist der Wert derselbe wie zuvor und wurde in- 376 zwischen weder ein Controller angemeldet, noch ein Controller invalidiert, 377 dann wird kein Wert weitergeleitet. Dadurch wird z.B. Flackern in 378 ListBoxen vermieden. 379 */ 380 381 { 382 // if ( pDispatch ) 383 // return; 384 SetState_Impl( eState, pState, bMaybeDirty ); 385 } 386 387 //-------------------------------------------------------------------- 388 389 void SfxStateCache::SetVisibleState( sal_Bool bShow ) 390 { 391 SfxItemState eState( SFX_ITEM_AVAILABLE ); 392 const SfxPoolItem* pState( NULL ); 393 sal_Bool bNotify( sal_False ); 394 sal_Bool bDeleteItem( sal_False ); 395 396 if ( bShow != bItemVisible ) 397 { 398 bItemVisible = bShow; 399 if ( bShow ) 400 { 401 if ( IsInvalidItem(pLastItem) || ( pLastItem == NULL )) 402 { 403 pState = new SfxVoidItem( nId ); 404 bDeleteItem = sal_True; 405 } 406 else 407 pState = pLastItem; 408 409 eState = eLastState; 410 bNotify = ( pState != 0 ); 411 } 412 else 413 { 414 pState = new SfxVisibilityItem( nId, sal_False ); 415 bDeleteItem = sal_True; 416 } 417 418 // Controller updaten 419 if ( !pDispatch && pController ) 420 { 421 for ( SfxControllerItem *pCtrl = pController; 422 pCtrl; 423 pCtrl = pCtrl->GetItemLink() ) 424 pCtrl->StateChanged( nId, eState, pState ); 425 } 426 427 if ( pInternalController ) 428 pInternalController->StateChanged( nId, eState, pState ); 429 430 if ( !bDeleteItem ) 431 delete pState; 432 } 433 } 434 435 //-------------------------------------------------------------------- 436 437 void SfxStateCache::SetState_Impl 438 ( 439 SfxItemState eState, // <SfxItemState> von 'pState' 440 const SfxPoolItem* pState, // Status des Slots, ggf. 0 oder -1 441 sal_Bool bMaybeDirty 442 ) 443 { 444 (void)bMaybeDirty; //unused 445 DBG_MEMTEST(); 446 DBG_CHKTHIS(SfxStateCache, 0); 447 448 // wenn zwischen Enter- und LeaveRegistrations ein hartes Update kommt 449 // k"onnen zwischenzeitlich auch Cached ohne Controller exisitieren 450 if ( !pController && !pInternalController ) 451 return; 452 453 DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" ); 454 // DBG_ASSERT( bCtrlDirty || ( aSlotServ.GetSlot() && aSlotServ.GetSlot()->IsMode(SFX_SLOT_VOLATILE) ), ! Discussed with MBA 455 // "setting state of non dirty controller" ); 456 DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState, "invalid SfxItemState" ); 457 DBG_PROFSTART(SfxStateCacheSetState); 458 459 // m"ussen die Controller "uberhaupt benachrichtigt werden? 460 bool bNotify = bItemDirty; 461 if ( !bItemDirty ) 462 { 463 bool bBothAvailable = pLastItem && pState && 464 !IsInvalidItem(pState) && !IsInvalidItem(pLastItem); 465 DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" ); 466 if ( bBothAvailable ) 467 bNotify = pState->Type() != pLastItem->Type() || 468 *pState != *pLastItem; 469 else 470 bNotify = ( pState != pLastItem ) || ( eState != eLastState ); 471 } 472 473 if ( bNotify ) 474 { 475 // Controller updaten 476 if ( !pDispatch && pController ) 477 { 478 for ( SfxControllerItem *pCtrl = pController; 479 pCtrl; 480 pCtrl = pCtrl->GetItemLink() ) 481 pCtrl->StateChanged( nId, eState, pState ); 482 } 483 484 if ( pInternalController ) 485 ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eState, pState, &aSlotServ ); 486 487 // neuen Wert merken 488 if ( !IsInvalidItem(pLastItem) ) 489 DELETEZ(pLastItem); 490 if ( pState && !IsInvalidItem(pState) ) 491 pLastItem = pState->Clone(); 492 else 493 pLastItem = 0; 494 eLastState = eState; 495 bItemDirty = sal_False; 496 } 497 498 bCtrlDirty = sal_False; 499 DBG_PROFSTOP(SfxStateCacheSetState); 500 } 501 502 503 //-------------------------------------------------------------------- 504 505 // alten Status in allen Controllern nochmal setzen 506 507 void SfxStateCache::SetCachedState( sal_Bool bAlways ) 508 { 509 DBG_MEMTEST(); 510 DBG_CHKTHIS(SfxStateCache, 0); 511 DBG_ASSERT(pController==NULL||pController->GetId()==nId, "Cache mit falschem ControllerItem" ); 512 DBG_PROFSTART(SfxStateCacheSetState); 513 514 // nur updaten wenn cached item vorhanden und auch verarbeitbar 515 // (Wenn der State gesendet wird, mu\s sichergestellt sein, da\s ein 516 // Slotserver vorhanden ist, s. SfxControllerItem::GetCoreMetric() ) 517 if ( bAlways || ( !bItemDirty && !bSlotDirty ) ) 518 { 519 // Controller updaten 520 if ( !pDispatch && pController ) 521 { 522 for ( SfxControllerItem *pCtrl = pController; 523 pCtrl; 524 pCtrl = pCtrl->GetItemLink() ) 525 pCtrl->StateChanged( nId, eLastState, pLastItem ); 526 } 527 528 if ( pInternalController ) 529 ((SfxDispatchController_Impl *)pInternalController)->StateChanged( nId, eLastState, pLastItem, &aSlotServ ); 530 531 // Controller sind jetzt ok 532 bCtrlDirty = sal_True; 533 } 534 535 DBG_PROFSTOP(SfxStateCacheSetState); 536 } 537 538 539 //-------------------------------------------------------------------- 540 541 // FloatingWindows in allen Controls mit dieser Id zerstoeren 542 543 void SfxStateCache::DeleteFloatingWindows() 544 { 545 DBG_MEMTEST(); 546 DBG_CHKTHIS(SfxStateCache, 0); 547 548 SfxControllerItem *pNextCtrl=0; 549 for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl ) 550 { 551 DBG_TRACE((ByteString("pCtrl: ").Append(ByteString::CreateFromInt64((sal_uIntPtr)pCtrl))).GetBuffer()); 552 pNextCtrl = pCtrl->GetItemLink(); 553 pCtrl->DeleteFloatingWindow(); 554 } 555 } 556 557 ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > SfxStateCache::GetDispatch() const 558 { 559 if ( pDispatch ) 560 return pDispatch->xDisp; 561 return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > (); 562 } 563 564 void SfxStateCache::Dispatch( const SfxItemSet* pSet, sal_Bool bForceSynchron ) 565 { 566 // protect pDispatch against destruction in the call 567 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XStatusListener > xKeepAlive( pDispatch ); 568 if ( pDispatch ) 569 { 570 uno::Sequence < beans::PropertyValue > aArgs; 571 if (pSet) 572 TransformItems( nId, *pSet, aArgs ); 573 pDispatch->Dispatch( aArgs, bForceSynchron ); 574 } 575 } 576 577 578