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