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         throw (uno::RuntimeException);
102 
103     //=====  lang::XEventListener  ============================================
104     virtual void SAL_CALL
105         disposing (const lang::EventObject& rEventObject)
106         throw (::com::sun::star::uno::RuntimeException);
107 
108     virtual void SAL_CALL disposing (void);
109 
110     /** This is only a fallback to make the office usable when for some
111         reason the intended way of unlocking it failed.
112     */
113     DECL_LINK(Timeout, void*);
114 
115     /** Convenience method that finds the layout manager associated with the
116         frame that shows the ViewShellBase.
117     */
118     Reference<frame::XLayoutManager> GetLayoutManager (void);
119 
120     Implementation (const Implementation&); // Not implemented.
121     Implementation& operator= (const Implementation&); // Not implemented.
122 };
123 
124 
125 
126 
127 //===== UpdateLockManager =====================================================
128 
UpdateLockManager(ViewShellBase & rBase)129 UpdateLockManager::UpdateLockManager (ViewShellBase& rBase)
130     : mpImpl(new Implementation(rBase))
131 {
132     mpImpl->acquire();
133 }
134 
135 
136 
~UpdateLockManager(void)137 UpdateLockManager::~UpdateLockManager (void)
138 {
139     if (mpImpl != NULL)
140     {
141         mpImpl->ForceUnlock();
142         mpImpl->release();
143     }
144 }
145 
146 
147 
148 
Disable(void)149 void UpdateLockManager::Disable (void)
150 {
151     if (mpImpl != NULL)
152     {
153         mpImpl->ForceUnlock();
154         mpImpl->release();
155         mpImpl = NULL;
156     }
157 }
158 
159 
160 
161 
Lock(void)162 void UpdateLockManager::Lock (void)
163 {
164     if (mpImpl != NULL)
165         mpImpl->Lock();
166 }
167 
168 
169 
170 
Unlock(void)171 void UpdateLockManager::Unlock (void)
172 {
173     if (mpImpl != NULL)
174         mpImpl->Unlock();
175 }
176 
177 
178 
179 
IsLocked(void) const180 bool UpdateLockManager::IsLocked (void) const
181 {
182     if (mpImpl != NULL)
183         return mpImpl->IsLocked();
184     else
185         return false;
186 }
187 
188 
189 
190 //===== UpdateLock::Implementation ============================================
191 
Implementation(ViewShellBase & rBase)192 UpdateLockManager::Implementation::Implementation (ViewShellBase& rBase)
193     : InterfaceBase(maMutex),
194       mrBase(rBase),
195       mnLockDepth(0),
196       maTimer(),
197       mbUnlockOnNextLayout(false),
198       mbListenerIsRegistered(false),
199       mbLayouterIsLocked(false)
200 {
201 }
202 
203 
204 
205 
~Implementation(void)206 UpdateLockManager::Implementation::~Implementation (void)
207 {
208     OSL_ASSERT(mnLockDepth==0);
209     ForceUnlock();
210 }
211 
212 
213 
214 
Lock(void)215 void UpdateLockManager::Implementation::Lock (void)
216 {
217     ++mnLockDepth;
218     if (mnLockDepth == 1)
219     {
220         Reference<frame::XLayoutManager> xLayouter (GetLayoutManager());
221         if (xLayouter.is())
222         {
223             // Register as event listener.
224             Reference<frame::XLayoutManagerEventBroadcaster> xBroadcaster (
225                 xLayouter, UNO_QUERY);
226             if (xBroadcaster.is())
227             {
228                 mbListenerIsRegistered = true;
229                 xBroadcaster->addLayoutManagerEventListener(
230                     Reference<frame::XLayoutManagerListener> (
231                         static_cast<XWeak*>(this), UNO_QUERY) );
232             }
233 
234             // Lock the layout manager.
235             mbLayouterIsLocked = true;
236             xLayouter->lock();
237         }
238 
239         // As a fallback, when the notification mechanism does not work (or is
240         // incorrectly used) we use a timer that will unlock us eventually.
241         maTimer.SetTimeout(5000 /*ms*/);
242         maTimer.SetTimeoutHdl(LINK(this,UpdateLockManager::Implementation,Timeout));
243         maTimer.Start();
244     }
245 }
246 
247 
248 
249 
Unlock(void)250 void UpdateLockManager::Implementation::Unlock (void)
251 {
252     --mnLockDepth;
253 
254     if (mnLockDepth == 0)
255     {
256         // Stop the timer.  We don't need it anymore.
257         maTimer.Stop();
258 
259         try
260         {
261             Reference<frame::XLayoutManager> xLayouter (GetLayoutManager());
262             if (xLayouter.is())
263             {
264                 // Detach from the layouter.
265                 if (mbListenerIsRegistered)
266                 {
267                     Reference<frame::XLayoutManagerEventBroadcaster> xBroadcaster (
268                         xLayouter, UNO_QUERY);
269                     if (xBroadcaster.is())
270                     {
271                         mbListenerIsRegistered = false;
272                         xBroadcaster->removeLayoutManagerEventListener(
273                             Reference<frame::XLayoutManagerListener> (
274                                 static_cast<XWeak*>(this), UNO_QUERY) );
275                     }
276                 }
277 
278                 // Unlock the layouter.
279                 if (mbLayouterIsLocked)
280                 {
281                     mbLayouterIsLocked = false;
282                     xLayouter->unlock();
283                 }
284             }
285         }
286         catch (RuntimeException)
287         { }
288 
289         // Force a rearrangement of the UI elements of the views.
290         mrBase.Rearrange();
291     }
292 }
293 
294 
295 
296 
IsLocked(void) const297 bool UpdateLockManager::Implementation::IsLocked (void) const
298 {
299     return (mnLockDepth > 0);
300 }
301 
302 
303 
304 
ForceUnlock(void)305 void UpdateLockManager::Implementation::ForceUnlock (void)
306 {
307     while (IsLocked())
308         Unlock();
309 }
310 
311 
312 
313 
layoutEvent(const lang::EventObject &,sal_Int16 eLayoutEvent,const Any & rInfo)314 void SAL_CALL UpdateLockManager::Implementation::layoutEvent (
315     const lang::EventObject&,
316     sal_Int16 eLayoutEvent,
317     const Any& rInfo)
318     throw (uno::RuntimeException)
319 {
320     switch (eLayoutEvent)
321     {
322         case frame::LayoutManagerEvents::LOCK:
323         {
324             sal_Int32 nLockCount;
325             rInfo >>= nLockCount;
326         }
327         break;
328 
329         case frame::LayoutManagerEvents::UNLOCK:
330         {
331             sal_Int32 nLockCount = 0;
332             rInfo >>= nLockCount;
333             if (nLockCount == 1)
334             {
335                 // The lock count dropped to one.  This means that we are
336                 // the only one that still holds a lock to the layout
337                 // manager.  We unlock the layout manager now and the
338                 // ViewShellBase on the next layout of the layout manager.
339                 mbUnlockOnNextLayout = true;
340                 Reference<frame::XLayoutManager> xLayouter (GetLayoutManager());
341                 if (xLayouter.is() && mbLayouterIsLocked)
342                 {
343                     mbLayouterIsLocked = false;
344                     xLayouter->unlock();
345                 }
346             }
347         }
348         break;
349 
350         case frame::LayoutManagerEvents::LAYOUT:
351             // Unlock when the layout manager is not still locked.
352             if (mbUnlockOnNextLayout)
353                 Unlock();
354             break;
355     }
356 }
357 
358 
359 
360 
disposing(const lang::EventObject &)361 void SAL_CALL UpdateLockManager::Implementation::disposing (const lang::EventObject& )
362     throw (::com::sun::star::uno::RuntimeException)
363 {
364 }
365 
366 
367 
368 
disposing(void)369 void SAL_CALL UpdateLockManager::Implementation::disposing (void)
370 {
371 }
372 
373 
374 
375 
IMPL_LINK(UpdateLockManager::Implementation,Timeout,void *,EMPTYARG)376 IMPL_LINK(UpdateLockManager::Implementation, Timeout, void*, EMPTYARG)
377 {
378     // This method is only called when all else failed.  We unlock
379     // regardless of how deep the lock depth.
380     while (mnLockDepth > 0)
381         Unlock();
382     return 1;
383 }
384 
385 
386 
387 
388 Reference< ::com::sun::star::frame::XLayoutManager>
GetLayoutManager(void)389     UpdateLockManager::Implementation::GetLayoutManager (void)
390 {
391     Reference<frame::XLayoutManager> xLayoutManager;
392 
393     if (mxLayoutManager.get() == NULL)
394     {
395         if (mrBase.GetViewFrame()!=NULL)
396         {
397             Reference<beans::XPropertySet> xFrameProperties (
398                 mrBase.GetViewFrame()->GetFrame().GetFrameInterface(),
399                 UNO_QUERY);
400             if (xFrameProperties.is())
401             {
402                 try
403                 {
404                     Any aValue (xFrameProperties->getPropertyValue(
405                         ::rtl::OUString::createFromAscii("LayoutManager")));
406                     aValue >>= xLayoutManager;
407                 }
408                 catch (const beans::UnknownPropertyException& rException)
409                 {
410                     (void)rException;
411                 }
412             }
413             mxLayoutManager = xLayoutManager;
414         }
415     }
416     else
417         xLayoutManager = mxLayoutManager;
418 
419     return xLayoutManager;
420 }
421 
422 
423 
424 
425 } // end of anonymous namespace
426