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