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
UpdateLockManager(ViewShellBase & rBase)127 UpdateLockManager::UpdateLockManager (ViewShellBase& rBase)
128 : mpImpl(new Implementation(rBase))
129 {
130 mpImpl->acquire();
131 }
132
133
134
~UpdateLockManager(void)135 UpdateLockManager::~UpdateLockManager (void)
136 {
137 if (mpImpl != NULL)
138 {
139 mpImpl->ForceUnlock();
140 mpImpl->release();
141 }
142 }
143
144
145
146
Disable(void)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
Lock(void)160 void UpdateLockManager::Lock (void)
161 {
162 if (mpImpl != NULL)
163 mpImpl->Lock();
164 }
165
166
167
168
Unlock(void)169 void UpdateLockManager::Unlock (void)
170 {
171 if (mpImpl != NULL)
172 mpImpl->Unlock();
173 }
174
175
176
177
IsLocked(void) const178 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
Implementation(ViewShellBase & rBase)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
~Implementation(void)204 UpdateLockManager::Implementation::~Implementation (void)
205 {
206 OSL_ASSERT(mnLockDepth==0);
207 ForceUnlock();
208 }
209
210
211
212
Lock(void)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
Unlock(void)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
IsLocked(void) const295 bool UpdateLockManager::Implementation::IsLocked (void) const
296 {
297 return (mnLockDepth > 0);
298 }
299
300
301
302
ForceUnlock(void)303 void UpdateLockManager::Implementation::ForceUnlock (void)
304 {
305 while (IsLocked())
306 Unlock();
307 }
308
309
310
311
layoutEvent(const lang::EventObject &,sal_Int16 eLayoutEvent,const Any & rInfo)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
disposing(const lang::EventObject &)358 void SAL_CALL UpdateLockManager::Implementation::disposing (const lang::EventObject& )
359 {
360 }
361
362
363
364
disposing(void)365 void SAL_CALL UpdateLockManager::Implementation::disposing (void)
366 {
367 }
368
369
370
371
IMPL_LINK(UpdateLockManager::Implementation,Timeout,void *,EMPTYARG)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>
GetLayoutManager(void)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