xref: /trunk/main/sd/source/ui/view/UpdateLockManager.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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