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_sd.hxx" 26 27 #include "UpdateLockManager.hxx" 28 29 #include "MutexOwner.hxx" 30 #include "ViewShellBase.hxx" 31 #include <com/sun/star/frame/XLayoutManager.hpp> 32 #include <com/sun/star/frame/XLayoutManagerEventBroadcaster.hpp> 33 #include <com/sun/star/frame/LayoutManagerEvents.hpp> 34 #include <com/sun/star/beans/XPropertySet.hpp> 35 #include <cppuhelper/compbase1.hxx> 36 37 #include <vcl/timer.hxx> 38 #include <sfx2/viewfrm.hxx> 39 40 using namespace ::com::sun::star::uno; 41 using namespace ::com::sun::star; 42 43 namespace { 44 typedef cppu::WeakComponentImplHelper1<frame::XLayoutManagerListener> InterfaceBase; 45 } 46 47 namespace sd { 48 49 50 /** This implementation class not only implements the Lock() and Unlock() 51 methods but as well listens for the right combination of events to call 52 Unlock() when all is ready after the PaneManager has switched (some of) 53 its view shells. 54 */ 55 56 class UpdateLockManager::Implementation 57 : protected MutexOwner, 58 public InterfaceBase 59 { 60 public: 61 Implementation (ViewShellBase& rBase); 62 virtual ~Implementation (void); 63 64 void Lock (void); 65 void Unlock (void); 66 bool IsLocked (void) const; 67 68 /** Unlock regardless of the current lock level. 69 */ 70 void ForceUnlock (void); 71 72 private: 73 ViewShellBase& mrBase; 74 /// A lock level greater than 0 indicates that the ViewShellBase is locked. 75 sal_Int32 mnLockDepth; 76 /// The emergency timer to unlock the ViewShellBase when all else fails. 77 Timer maTimer; 78 /// Remember when to unlock after a layout event from frame::XLayoutManager 79 bool mbUnlockOnNextLayout; 80 /// Remember whether we are listening to the frame::XLayoutManager 81 bool mbListenerIsRegistered; 82 /// Remember whether the frame::XLayoutManager is locked. 83 bool mbLayouterIsLocked; 84 /** We hold a weak reference to the layout manager in order to have 85 access to it even when the ViewShellBase object is not valid anymore 86 and can not be used to obtain the layout manager. 87 */ 88 WeakReference<frame::XLayoutManager> mxLayoutManager; 89 90 //===== frame::XLayoutEventListener ===================================== 91 92 /** The event of the layouter are observed to find the best moment for 93 unlocking. This is the first layout after the lock level of the 94 layouter drops to one (we hold a lock to it ourselves which we 95 release when unlocking). 96 */ 97 virtual void SAL_CALL layoutEvent ( 98 const lang::EventObject& xSource, 99 sal_Int16 eLayoutEvent, 100 const Any& rInfo); 101 102 //===== lang::XEventListener ============================================ 103 virtual void SAL_CALL 104 disposing (const lang::EventObject& rEventObject); 105 106 virtual void SAL_CALL disposing (void); 107 108 /** This is only a fallback to make the office usable when for some 109 reason the intended way of unlocking it failed. 110 */ 111 DECL_LINK(Timeout, void*); 112 113 /** Convenience method that finds the layout manager associated with the 114 frame that shows the ViewShellBase. 115 */ 116 Reference<frame::XLayoutManager> GetLayoutManager (void); 117 118 Implementation (const Implementation&); // Not implemented. 119 Implementation& operator= (const Implementation&); // Not implemented. 120 }; 121 122 123 124 125 //===== UpdateLockManager ===================================================== 126 127 UpdateLockManager::UpdateLockManager (ViewShellBase& rBase) 128 : mpImpl(new Implementation(rBase)) 129 { 130 mpImpl->acquire(); 131 } 132 133 134 135 UpdateLockManager::~UpdateLockManager (void) 136 { 137 if (mpImpl != NULL) 138 { 139 mpImpl->ForceUnlock(); 140 mpImpl->release(); 141 } 142 } 143 144 145 146 147 void UpdateLockManager::Disable (void) 148 { 149 if (mpImpl != NULL) 150 { 151 mpImpl->ForceUnlock(); 152 mpImpl->release(); 153 mpImpl = NULL; 154 } 155 } 156 157 158 159 160 void UpdateLockManager::Lock (void) 161 { 162 if (mpImpl != NULL) 163 mpImpl->Lock(); 164 } 165 166 167 168 169 void UpdateLockManager::Unlock (void) 170 { 171 if (mpImpl != NULL) 172 mpImpl->Unlock(); 173 } 174 175 176 177 178 bool UpdateLockManager::IsLocked (void) const 179 { 180 if (mpImpl != NULL) 181 return mpImpl->IsLocked(); 182 else 183 return false; 184 } 185 186 187 188 //===== UpdateLock::Implementation ============================================ 189 190 UpdateLockManager::Implementation::Implementation (ViewShellBase& rBase) 191 : InterfaceBase(maMutex), 192 mrBase(rBase), 193 mnLockDepth(0), 194 maTimer(), 195 mbUnlockOnNextLayout(false), 196 mbListenerIsRegistered(false), 197 mbLayouterIsLocked(false) 198 { 199 } 200 201 202 203 204 UpdateLockManager::Implementation::~Implementation (void) 205 { 206 OSL_ASSERT(mnLockDepth==0); 207 ForceUnlock(); 208 } 209 210 211 212 213 void UpdateLockManager::Implementation::Lock (void) 214 { 215 ++mnLockDepth; 216 if (mnLockDepth == 1) 217 { 218 Reference<frame::XLayoutManager> xLayouter (GetLayoutManager()); 219 if (xLayouter.is()) 220 { 221 // Register as event listener. 222 Reference<frame::XLayoutManagerEventBroadcaster> xBroadcaster ( 223 xLayouter, UNO_QUERY); 224 if (xBroadcaster.is()) 225 { 226 mbListenerIsRegistered = true; 227 xBroadcaster->addLayoutManagerEventListener( 228 Reference<frame::XLayoutManagerListener> ( 229 static_cast<XWeak*>(this), UNO_QUERY) ); 230 } 231 232 // Lock the layout manager. 233 mbLayouterIsLocked = true; 234 xLayouter->lock(); 235 } 236 237 // As a fallback, when the notification mechanism does not work (or is 238 // incorrectly used) we use a timer that will unlock us eventually. 239 maTimer.SetTimeout(5000 /*ms*/); 240 maTimer.SetTimeoutHdl(LINK(this,UpdateLockManager::Implementation,Timeout)); 241 maTimer.Start(); 242 } 243 } 244 245 246 247 248 void UpdateLockManager::Implementation::Unlock (void) 249 { 250 --mnLockDepth; 251 252 if (mnLockDepth == 0) 253 { 254 // Stop the timer. We don't need it anymore. 255 maTimer.Stop(); 256 257 try 258 { 259 Reference<frame::XLayoutManager> xLayouter (GetLayoutManager()); 260 if (xLayouter.is()) 261 { 262 // Detach from the layouter. 263 if (mbListenerIsRegistered) 264 { 265 Reference<frame::XLayoutManagerEventBroadcaster> xBroadcaster ( 266 xLayouter, UNO_QUERY); 267 if (xBroadcaster.is()) 268 { 269 mbListenerIsRegistered = false; 270 xBroadcaster->removeLayoutManagerEventListener( 271 Reference<frame::XLayoutManagerListener> ( 272 static_cast<XWeak*>(this), UNO_QUERY) ); 273 } 274 } 275 276 // Unlock the layouter. 277 if (mbLayouterIsLocked) 278 { 279 mbLayouterIsLocked = false; 280 xLayouter->unlock(); 281 } 282 } 283 } 284 catch (RuntimeException) 285 { } 286 287 // Force a rearrangement of the UI elements of the views. 288 mrBase.Rearrange(); 289 } 290 } 291 292 293 294 295 bool UpdateLockManager::Implementation::IsLocked (void) const 296 { 297 return (mnLockDepth > 0); 298 } 299 300 301 302 303 void UpdateLockManager::Implementation::ForceUnlock (void) 304 { 305 while (IsLocked()) 306 Unlock(); 307 } 308 309 310 311 312 void SAL_CALL UpdateLockManager::Implementation::layoutEvent ( 313 const lang::EventObject&, 314 sal_Int16 eLayoutEvent, 315 const Any& rInfo) 316 { 317 switch (eLayoutEvent) 318 { 319 case frame::LayoutManagerEvents::LOCK: 320 { 321 sal_Int32 nLockCount; 322 rInfo >>= nLockCount; 323 } 324 break; 325 326 case frame::LayoutManagerEvents::UNLOCK: 327 { 328 sal_Int32 nLockCount = 0; 329 rInfo >>= nLockCount; 330 if (nLockCount == 1) 331 { 332 // The lock count dropped to one. This means that we are 333 // the only one that still holds a lock to the layout 334 // manager. We unlock the layout manager now and the 335 // ViewShellBase on the next layout of the layout manager. 336 mbUnlockOnNextLayout = true; 337 Reference<frame::XLayoutManager> xLayouter (GetLayoutManager()); 338 if (xLayouter.is() && mbLayouterIsLocked) 339 { 340 mbLayouterIsLocked = false; 341 xLayouter->unlock(); 342 } 343 } 344 } 345 break; 346 347 case frame::LayoutManagerEvents::LAYOUT: 348 // Unlock when the layout manager is not still locked. 349 if (mbUnlockOnNextLayout) 350 Unlock(); 351 break; 352 } 353 } 354 355 356 357 358 void SAL_CALL UpdateLockManager::Implementation::disposing (const lang::EventObject& ) 359 { 360 } 361 362 363 364 365 void SAL_CALL UpdateLockManager::Implementation::disposing (void) 366 { 367 } 368 369 370 371 372 IMPL_LINK(UpdateLockManager::Implementation, Timeout, void*, EMPTYARG) 373 { 374 // This method is only called when all else failed. We unlock 375 // regardless of how deep the lock depth. 376 while (mnLockDepth > 0) 377 Unlock(); 378 return 1; 379 } 380 381 382 383 384 Reference< ::com::sun::star::frame::XLayoutManager> 385 UpdateLockManager::Implementation::GetLayoutManager (void) 386 { 387 Reference<frame::XLayoutManager> xLayoutManager; 388 389 if (mxLayoutManager.get() == NULL) 390 { 391 if (mrBase.GetViewFrame()!=NULL) 392 { 393 Reference<beans::XPropertySet> xFrameProperties ( 394 mrBase.GetViewFrame()->GetFrame().GetFrameInterface(), 395 UNO_QUERY); 396 if (xFrameProperties.is()) 397 { 398 try 399 { 400 Any aValue (xFrameProperties->getPropertyValue( 401 ::rtl::OUString::createFromAscii("LayoutManager"))); 402 aValue >>= xLayoutManager; 403 } 404 catch (const beans::UnknownPropertyException& rException) 405 { 406 (void)rException; 407 } 408 } 409 mxLayoutManager = xLayoutManager; 410 } 411 } 412 else 413 xLayoutManager = mxLayoutManager; 414 415 return xLayoutManager; 416 } 417 418 419 420 421 } // end of anonymous namespace 422