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 "ViewShellManager.hxx"
32 #include "ViewShell.hxx"
33 #include "ViewShellBase.hxx"
34 #include "Window.hxx"
35 #include "DrawDocShell.hxx"
36 #include "FormShellManager.hxx"
37 
38 #include <sfx2/dispatch.hxx>
39 #include <svx/svxids.hrc>
40 #include <svx/fmshell.hxx>
41 
42 #include <hash_map>
43 
44 #undef VERBOSE
45 //#define VERBOSE 2
46 
47 namespace sd {
48 
49 namespace {
50 
51 /** The ShellDescriptor class is used to shells together with their ids and
52     the factory that was used to create the shell.
53 
54     The shell pointer may be NULL.  In that case the shell is created on
55     demand by a factory.
56 
57     The factory pointer may be NULL.  In that case the shell pointer is
58     given to the ViewShellManager.
59 
60     Shell pointer and factory pointer can but should not be NULL at the same
61     time.
62 */
63 class ShellDescriptor {
64 public:
65     SfxShell* mpShell;
66     ShellId mnId;
67     ViewShellManager::SharedShellFactory mpFactory;
68     ShellDescriptor ();
69     ShellDescriptor (SfxShell* pShell, ShellId nId);
70     ShellDescriptor (const ShellDescriptor& rDescriptor);
71     ShellDescriptor& operator= (const ShellDescriptor& rDescriptor);
72     bool IsMainViewShell (void) const;
73     ::Window* GetWindow (void) const;
74 };
75 
76 
77 
78 
79 /** This functor can be used to search for a shell in an STL container when the
80     shell pointer is given.
81 */
82 class IsShell : public ::std::unary_function<ShellDescriptor,bool>
83 {
84 public:
85     IsShell (const SfxShell* pShell) : mpShell(pShell) {}
86     bool operator() (const ShellDescriptor& rDescriptor)
87     { return rDescriptor.mpShell == mpShell; }
88 private:
89     const SfxShell* mpShell;
90 };
91 
92 
93 
94 
95 /** This functor can be used to search for a shell in an STL container when the
96     id of the shell is given.
97 */
98 class IsId : public ::std::unary_function<ShellDescriptor,bool>
99 {
100 public:
101     IsId (ShellId nId) : mnId(nId) {}
102     bool operator() (const ShellDescriptor& rDescriptor)
103     { return rDescriptor.mnId == mnId; }
104 private:
105     ShellId mnId;
106 };
107 
108 } // end of anonymous namespace
109 
110 
111 
112 
113 class ViewShellManager::Implementation
114 {
115 public:
116     Implementation (
117         ViewShellManager& rManager,
118         ViewShellBase& rBase);
119     ~Implementation (void);
120 
121     void AddShellFactory (
122         const SfxShell* pViewShell,
123         const SharedShellFactory& rpFactory);
124     void RemoveShellFactory (
125         const SfxShell* pViewShell,
126         const SharedShellFactory& rpFactory);
127     void ActivateViewShell (
128         ViewShell* pViewShell);
129     void DeactivateViewShell (const ViewShell& rShell);
130     void ActivateShell (SfxShell& rShell);
131     void DeactivateShell (const SfxShell& rShell);
132     void ActivateShell (const ShellDescriptor& rDescriptor);
133     void SetFormShell (const ViewShell* pViewShell, FmFormShell* pFormShell, bool bAbove);
134     void ActivateSubShell (const SfxShell& rParentShell, ShellId nId);
135     void DeactivateSubShell (const SfxShell& rParentShell, ShellId nId);
136     void MoveSubShellToTop (const SfxShell& rParentShell, ShellId nId);
137     void MoveToTop (const SfxShell& rParentShell);
138     SfxShell* GetShell (ShellId nId) const;
139     SfxShell* GetTopShell (void) const;
140     void Shutdown (void);
141     void InvalidateAllSubShells (const SfxShell* pParentShell);
142 
143     /** Remove all shells from the SFX stack above and including the given
144         shell.
145     */
146     void TakeShellsFromStack (const SfxShell* pShell);
147 
148     class UpdateLock
149     {
150     public:
151         UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();}
152         ~UpdateLock (void) {mrImpl.UnlockUpdate();};
153     private:
154         Implementation& mrImpl;
155     };
156 
157 
158 
159     /** Prevent updates of the shell stack.  While the sub shell manager is
160         locked it will update its internal data structures but not alter the
161         shell stack.  Use this method when there are several modifications
162         to the shell stack to prevent multiple rebuilds of the shell stack
163         and resulting broadcasts.
164     */
165     void LockUpdate (void);
166 
167     /** Allow updates of the shell stack.  This method has to be called the
168         same number of times as LockUpdate() to really allow a rebuild of
169         the shell stack.
170     */
171     void UnlockUpdate (void);
172 
173 private:
174     ViewShellBase& mrBase;
175     mutable ::osl::Mutex maMutex;
176 
177     class ShellHash{public: size_t operator()(const SfxShell* p) const { return (size_t)p;} };
178     typedef ::std::hash_multimap<const SfxShell*,SharedShellFactory,ShellHash>
179         FactoryList;
180     FactoryList maShellFactories;
181 
182     /** List of the active view shells.  In order to create gather all shells
183         to put on the shell stack each view shell in this list is asked for
184         its sub-shells (typically toolbars).
185     */
186     typedef ::std::list<ShellDescriptor> ActiveShellList;
187     ActiveShellList maActiveViewShells;
188 
189     typedef ::std::list<ShellDescriptor> SubShellSubList;
190     typedef ::std::hash_map<const SfxShell*,SubShellSubList,ShellHash> SubShellList;
191     SubShellList maActiveSubShells;
192 
193     /** In this member we remember what shells we have pushed on the shell
194         stack.
195     */
196     typedef ::std::vector<SfxShell*> ShellStack;
197 
198     int mnUpdateLockCount;
199 
200     /** When this flag is set then the main view shell is always kept at the
201         top of the shell stack.
202     */
203     bool mbKeepMainViewShellOnTop;
204 
205     /** The UpdateShellStack() method can be called recursively.  This flag
206         is used to communicate between different levels of invocation: if
207         the stack has been updated in an inner call the outer call can (has
208         to) stop and return immediately.
209     */
210     bool mbShellStackIsUpToDate;
211 
212     SfxShell* mpFormShell;
213     const ViewShell* mpFormShellParent;
214     bool mbFormShellAboveParent;
215 
216     SfxShell* mpTopShell;
217 
218     void GatherActiveShells (ShellStack& rShellList);
219 
220     void UpdateShellStack (void);
221 
222     void CreateShells (void);
223 
224     /** This method rebuilds the stack of shells that are stacked upon the
225         view shell base.
226     */
227     void CreateTargetStack (ShellStack& rStack) const;
228 
229     DECL_LINK(WindowEventHandler, VclWindowEvent*);
230 
231 #ifdef VERBOSE
232     void DumpShellStack (const ShellStack& rStack);
233     void DumpSfxShellStack (void);
234 #endif
235 
236     /** To be called before a shell is taken fom the SFX shell stack.  This
237         method deactivates an active text editing to avoid problems with
238         undo managers.
239         Afterwards the Deactivate() of the shell is called.
240     */
241     void Deactivate (SfxShell* pShell);
242 
243     ShellDescriptor CreateSubShell (
244         SfxShell* pShell,
245         ShellId nShellId,
246         ::Window* pParentWindow,
247         FrameView* pFrameView);
248     void DestroyViewShell (const ShellDescriptor& rDescriptor);
249     void DestroySubShell (
250         const SfxShell& rViewShell,
251         const ShellDescriptor& rDescriptor);
252 };
253 
254 
255 
256 
257 //===== ViewShellManager ======================================================
258 
259 ViewShellManager::ViewShellManager (ViewShellBase& rBase)
260     : mpImpl(new Implementation(*this,rBase)),
261       mbValid(true)
262 {
263 }
264 
265 
266 
267 
268 ViewShellManager::~ViewShellManager (void)
269 {
270 }
271 
272 
273 
274 
275 void ViewShellManager::AddSubShellFactory (
276     ViewShell* pViewShell,
277     const SharedShellFactory& rpFactory)
278 {
279     if (mbValid)
280         mpImpl->AddShellFactory(pViewShell, rpFactory);
281 }
282 
283 
284 
285 
286 void ViewShellManager::RemoveSubShellFactory (
287     ViewShell* pViewShell,
288     const SharedShellFactory& rpFactory)
289 {
290     if (mbValid)
291         mpImpl->RemoveShellFactory(pViewShell, rpFactory);
292 }
293 
294 
295 
296 
297 void ViewShellManager::ActivateViewShell (ViewShell* pViewShell)
298 {
299     if (mbValid)
300         return mpImpl->ActivateViewShell(pViewShell);
301 }
302 
303 
304 
305 
306 void ViewShellManager::DeactivateViewShell (const ViewShell* pShell)
307 {
308     if (mbValid && pShell!=NULL)
309         mpImpl->DeactivateViewShell(*pShell);
310 }
311 
312 
313 
314 
315 void ViewShellManager::MoveSubShellToTop (
316     const ViewShell& rParentShell,
317     ShellId nId)
318 {
319     if (mbValid)
320         mpImpl->MoveSubShellToTop(rParentShell, nId);
321 }
322 
323 
324 
325 
326 void ViewShellManager::SetFormShell (
327     const ViewShell* pParentShell,
328     FmFormShell* pFormShell,
329     bool bAbove)
330 {
331     if (mbValid)
332         mpImpl->SetFormShell(pParentShell,pFormShell,bAbove);
333 }
334 
335 
336 
337 
338 void ViewShellManager::ActivateSubShell (const ViewShell& rViewShell, ShellId nId)
339 {
340     if (mbValid)
341         mpImpl->ActivateSubShell(rViewShell,nId);
342 }
343 
344 
345 
346 
347 void ViewShellManager::DeactivateSubShell (const ViewShell& rViewShell, ShellId nId)
348 {
349     if (mbValid)
350         mpImpl->DeactivateSubShell(rViewShell,nId);
351 }
352 
353 
354 
355 
356 void ViewShellManager::InvalidateAllSubShells (ViewShell* pViewShell)
357 {
358     if (mbValid)
359         mpImpl->InvalidateAllSubShells(pViewShell);
360 }
361 
362 
363 
364 
365 void ViewShellManager::ActivateShell (SfxShell* pShell)
366 {
367     if (mbValid && pShell!=NULL)
368         mpImpl->ActivateShell(*pShell);
369 }
370 
371 
372 
373 
374 void ViewShellManager::DeactivateShell (const SfxShell* pShell)
375 {
376     if (mbValid && pShell!=NULL)
377         mpImpl->DeactivateShell(*pShell);
378 }
379 
380 
381 
382 
383 void ViewShellManager::MoveToTop (const ViewShell& rParentShell)
384 {
385     if (mbValid)
386         mpImpl->MoveToTop(rParentShell);
387 }
388 
389 
390 
391 
392 SfxShell* ViewShellManager::GetShell (ShellId nId) const
393 {
394     if (mbValid)
395         return mpImpl->GetShell(nId);
396     else
397         return NULL;
398 }
399 
400 
401 
402 
403 SfxShell* ViewShellManager::GetTopShell (void) const
404 {
405     if (mbValid)
406         return mpImpl->GetTopShell();
407     else
408         return NULL;
409 }
410 
411 
412 
413 
414 void ViewShellManager::Shutdown (void)
415 {
416     if (mbValid)
417     {
418         mpImpl->Shutdown();
419         mbValid = false;
420     }
421 }
422 
423 
424 
425 void ViewShellManager::LockUpdate (void)
426 {
427     mpImpl->LockUpdate();
428 }
429 
430 
431 
432 
433 void ViewShellManager::UnlockUpdate (void)
434 {
435     mpImpl->UnlockUpdate();
436 }
437 
438 
439 
440 
441 //===== ViewShellManager::Implementation ======================================
442 
443 ViewShellManager::Implementation::Implementation (
444     ViewShellManager& rManager,
445     ViewShellBase& rBase)
446     : mrBase(rBase),
447       maMutex(),
448       maShellFactories(),
449       maActiveViewShells(),
450       mnUpdateLockCount(0),
451       mbKeepMainViewShellOnTop(false),
452       mbShellStackIsUpToDate(true),
453       mpFormShell(NULL),
454       mpFormShellParent(NULL),
455       mbFormShellAboveParent(true),
456       mpTopShell(NULL)
457 {
458     (void)rManager;
459 }
460 
461 
462 
463 
464 ViewShellManager::Implementation::~Implementation (void)
465 {
466     Shutdown();
467 }
468 
469 
470 
471 
472 void ViewShellManager::Implementation::AddShellFactory (
473     const SfxShell* pViewShell,
474     const SharedShellFactory& rpFactory)
475 {
476     bool bAlreadyAdded (false);
477 
478     // Check that the given factory has not already been added.
479     ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
480         maShellFactories.equal_range(pViewShell));
481     for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
482         if (iFactory->second == rpFactory)
483         {
484             bAlreadyAdded = true;
485             break;
486         }
487 
488     // Add the factory if it is not already present.
489     if ( ! bAlreadyAdded)
490         maShellFactories.insert(FactoryList::value_type(pViewShell, rpFactory));
491 }
492 
493 
494 
495 
496 void ViewShellManager::Implementation::RemoveShellFactory (
497     const SfxShell* pViewShell,
498     const SharedShellFactory& rpFactory)
499 {
500     ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
501         maShellFactories.equal_range(pViewShell));
502     for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
503         if (iFactory->second == rpFactory)
504         {
505             maShellFactories.erase(iFactory);
506             break;
507         }
508 }
509 
510 
511 
512 
513 void ViewShellManager::Implementation::ActivateViewShell (ViewShell* pViewShell)
514 {
515     ::osl::MutexGuard aGuard (maMutex);
516 
517     ShellDescriptor aResult;
518     aResult.mpShell = pViewShell;
519 
520     // Register as window listener so that the shells of the current
521     // window can be moved to the top of the shell stack.
522     if (aResult.mpShell != NULL)
523     {
524         ::Window* pWindow = aResult.GetWindow();
525         if (pWindow != NULL)
526             pWindow->AddEventListener(
527                 LINK(this, ViewShellManager::Implementation, WindowEventHandler));
528         else
529         {
530             DBG_ASSERT(false,
531                 "ViewShellManager::ActivateViewShell: "
532                 "new view shell has no active window");
533         }
534     }
535 
536     ActivateShell(aResult);
537 }
538 
539 
540 
541 
542 void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell& rShell)
543 {
544     ::osl::MutexGuard aGuard (maMutex);
545 
546     ActiveShellList::iterator iShell (::std::find_if (
547         maActiveViewShells.begin(),
548         maActiveViewShells.end(),
549         IsShell(&rShell)));
550     if (iShell != maActiveViewShells.end())
551     {
552         UpdateLock aLocker (*this);
553 
554         ShellDescriptor aDescriptor(*iShell);
555         mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell));
556         maActiveViewShells.erase(iShell);
557         TakeShellsFromStack(aDescriptor.mpShell);
558 
559         // Deactivate sub shells.
560         SubShellList::iterator iList (maActiveSubShells.find(&rShell));
561         if (iList != maActiveSubShells.end())
562         {
563             SubShellSubList& rList (iList->second);
564             while ( ! rList.empty())
565                 DeactivateSubShell(rShell, rList.front().mnId);
566         }
567 
568         DestroyViewShell(aDescriptor);
569     }
570 }
571 
572 
573 
574 
575 void ViewShellManager::Implementation::ActivateShell (SfxShell& rShell)
576 {
577     ::osl::MutexGuard aGuard (maMutex);
578 
579     // Create a new shell or recycle on in the cache.
580     ShellDescriptor aDescriptor;
581     aDescriptor.mpShell = &rShell;
582 
583     ActivateShell(aDescriptor);
584 }
585 
586 
587 
588 
589 void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor)
590 {
591     // Put shell on top of the active view shells.
592     if (rDescriptor.mpShell != NULL)
593     {
594         // Determine where to put the view shell on the stack.  By default
595         // it is put on top of the stack.  When the view shell of the center
596         // pane is to be kept top most and the new view shell is not
597         // displayed in the center pane then it is inserted at the position
598         // one below the top.
599         ActiveShellList::iterator iInsertPosition (maActiveViewShells.begin());
600         if (iInsertPosition != maActiveViewShells.end()
601             && mbKeepMainViewShellOnTop
602             && ! rDescriptor.IsMainViewShell()
603             && iInsertPosition->IsMainViewShell())
604         {
605             ++iInsertPosition;
606         }
607         maActiveViewShells.insert(
608             iInsertPosition,
609             rDescriptor);
610     }
611 }
612 
613 
614 
615 
616 void ViewShellManager::Implementation::DeactivateShell (const SfxShell& rShell)
617 {
618     ::osl::MutexGuard aGuard (maMutex);
619 
620     ActiveShellList::iterator iShell (::std::find_if (
621         maActiveViewShells.begin(),
622         maActiveViewShells.end(),
623         IsShell(&rShell)));
624     if (iShell != maActiveViewShells.end())
625     {
626         UpdateLock aLocker (*this);
627 
628         ShellDescriptor aDescriptor(*iShell);
629         mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell));
630         maActiveViewShells.erase(iShell);
631         TakeShellsFromStack(aDescriptor.mpShell);
632 
633         // Deactivate sub shells.
634         SubShellList::iterator iList (maActiveSubShells.find(&rShell));
635         if (iList != maActiveSubShells.end())
636         {
637             SubShellSubList& rList (iList->second);
638             while ( ! rList.empty())
639                 DeactivateSubShell(rShell, rList.front().mnId);
640         }
641 
642         DestroyViewShell(aDescriptor);
643     }
644 }
645 
646 
647 
648 
649 void ViewShellManager::Implementation::ActivateSubShell (
650     const SfxShell& rParentShell,
651     ShellId nId)
652 {
653     ::osl::MutexGuard aGuard (maMutex);
654 
655     do
656     {
657         // Check that the given view shell is active.
658         ActiveShellList::iterator iShell (::std::find_if (
659             maActiveViewShells.begin(),
660             maActiveViewShells.end(),
661             IsShell(&rParentShell)));
662         if (iShell == maActiveViewShells.end())
663             break;
664 
665         // Create the sub shell list if it does not yet exist.
666         SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
667         if (iList == maActiveSubShells.end())
668             iList = maActiveSubShells.insert(
669                 SubShellList::value_type(&rParentShell,SubShellSubList())).first;
670 
671         // Do not activate an object bar that is already active.  Requesting
672         // this is not exactly an error but may be an indication of one.
673         SubShellSubList& rList (iList->second);
674         if (::std::find_if(rList.begin(),rList.end(), IsId(nId)) != rList.end())
675             break;
676 
677         // Add just the id of the sub shell. The actual shell is created
678         // later in CreateShells().
679         UpdateLock aLock (*this);
680         rList.push_back(ShellDescriptor(NULL, nId));
681     }
682     while (false);
683 }
684 
685 
686 
687 
688 void ViewShellManager::Implementation::DeactivateSubShell (
689     const SfxShell& rParentShell,
690     ShellId nId)
691 {
692     ::osl::MutexGuard aGuard (maMutex);
693 
694     do
695     {
696         // Check that the given view shell is active.
697         SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
698         if (iList == maActiveSubShells.end())
699             break;
700 
701         // Look up the sub shell.
702         SubShellSubList& rList (iList->second);
703         SubShellSubList::iterator iShell (
704             ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
705         if (iShell == rList.end())
706             break;
707         SfxShell* pShell = iShell->mpShell;
708         if (pShell == NULL)
709             break;
710 
711         UpdateLock aLock (*this);
712 
713         ShellDescriptor aDescriptor(*iShell);
714         // Remove the sub shell from both the internal structure as well as the
715         // SFX shell stack above and including the sub shell.
716         rList.erase(iShell);
717         TakeShellsFromStack(pShell);
718 
719         DestroySubShell(rParentShell, aDescriptor);
720     }
721     while (false);
722 }
723 
724 
725 
726 
727 void ViewShellManager::Implementation::MoveSubShellToTop (
728     const SfxShell& rParentShell,
729     ShellId nId)
730 {
731     SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
732     if (iList != maActiveSubShells.end())
733     {
734         // Look up the sub shell.
735         SubShellSubList& rList (iList->second);
736         SubShellSubList::iterator iShell (
737             ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
738         if (iShell!=rList.end() && iShell!=rList.begin())
739         {
740             SubShellSubList::value_type aEntry (*iShell);
741             rList.erase(iShell);
742             rList.push_front(aEntry);
743         }
744     }
745     else
746     {
747         // Ignore this call when there are no sub shells for the given
748         // parent shell.  We could remember the sub shell to move to the top
749         // but we do not.  Do call this method at a later time instead.
750     }
751 }
752 
753 
754 
755 void ViewShellManager::Implementation::MoveToTop (const SfxShell& rShell)
756 {
757     ::osl::MutexGuard aGuard (maMutex);
758 
759     // Check that we have access to a dispatcher.  If not, then we are
760     // (probably) called while the view shell is still being created or
761     // initialized.  Without dispatcher we can not rebuild the shell stack
762     // to move the requested shell to the top.  So return right away instead
763     // of making a mess without being able to clean up afterwards.
764     if (mrBase.GetDispatcher() == NULL)
765         return;
766 
767     ActiveShellList::iterator iShell (::std::find_if (
768         maActiveViewShells.begin(),
769         maActiveViewShells.end(),
770         IsShell(&rShell)));
771     bool bMove = true;
772     if (iShell != maActiveViewShells.end())
773     {
774         // Is the shell already at the top of the stack?  We have to keep
775         // the case in mind that mbKeepMainViewShellOnTop is true.  Shells
776         // that are not the main view shell are placed on the second-to-top
777         // position in this case.
778             if (iShell == maActiveViewShells.begin()
779             && (iShell->IsMainViewShell() || ! mbKeepMainViewShellOnTop))
780         {
781             // The shell is at the top position and is either a) the main
782             // view shell or b) another shell but the main view shell is not
783             // kept at the top position.  We do not have to move the shell.
784             bMove = false;
785         }
786         else if (iShell == ++maActiveViewShells.begin()
787             && ! iShell->IsMainViewShell()
788             && mbKeepMainViewShellOnTop)
789         {
790             // The shell is a the second-to-top position, not the main view
791             // shell and the main view shell is kept at the top position.
792             // Therefore we do not have to move the shell.
793             bMove = false;
794         }
795     }
796     else
797     {
798         // The shell is not on the stack.  Therefore it can not be moved.
799         // We could insert it but we don't.  Use ActivateViewShell() for
800         // that.
801         bMove = false;
802     }
803 
804     // When the shell is not at the right position it is removed from the
805     // internal list of shells and inserted at the correct position.
806     if (bMove)
807     {
808         UpdateLock aLock (*this);
809 
810         ShellDescriptor aDescriptor(*iShell);
811 
812         TakeShellsFromStack(&rShell);
813         maActiveViewShells.erase(iShell);
814 
815         // Find out whether to insert at the top or one below.
816         ActiveShellList::iterator aInsertPosition (maActiveViewShells.begin());
817         if (mbKeepMainViewShellOnTop && ! aDescriptor.IsMainViewShell())
818         {
819             if (maActiveViewShells.back().IsMainViewShell())
820                 aInsertPosition++;
821         }
822 
823         maActiveViewShells.insert(aInsertPosition, aDescriptor);
824     }
825 }
826 
827 
828 
829 
830 SfxShell* ViewShellManager::Implementation::GetShell (ShellId nId) const
831 {
832     ::osl::MutexGuard aGuard (maMutex);
833 
834     SfxShell* pShell = NULL;
835 
836     // First search the active view shells.
837     ActiveShellList::const_iterator iShell (
838         ::std::find_if (
839         maActiveViewShells.begin(),
840         maActiveViewShells.end(),
841         IsId(nId)));
842     if (iShell != maActiveViewShells.end())
843         pShell = iShell->mpShell;
844     else
845     {
846         // Now search the active sub shells of every active view shell.
847         SubShellList::const_iterator iList;
848         for (iList=maActiveSubShells.begin(); iList!=maActiveSubShells.end(); ++iList)
849         {
850             const SubShellSubList& rList (iList->second);
851             SubShellSubList::const_iterator iSubShell(
852                 ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
853             if (iSubShell != rList.end())
854             {
855                 pShell = iSubShell->mpShell;
856                 break;
857             }
858         }
859     }
860 
861     return pShell;
862 }
863 
864 
865 
866 
867 SfxShell* ViewShellManager::Implementation::GetTopShell (void) const
868 {
869     OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0));
870     return mpTopShell;
871 }
872 
873 
874 
875 
876 void ViewShellManager::Implementation::LockUpdate (void)
877 {
878     mnUpdateLockCount++;
879 }
880 
881 
882 
883 
884 void ViewShellManager::Implementation::UnlockUpdate (void)
885 {
886     ::osl::MutexGuard aGuard (maMutex);
887 
888     mnUpdateLockCount--;
889     if (mnUpdateLockCount < 0)
890     {
891         // This should not happen.
892         OSL_ASSERT (mnUpdateLockCount>=0);
893         mnUpdateLockCount = 0;
894     }
895     if (mnUpdateLockCount == 0)
896         UpdateShellStack();
897 }
898 
899 
900 
901 
902 /** Update the SFX shell stack (the portion that is visible to us) so that
903     it matches the internal shell stack.  This is done in six steps:
904     1. Create the missing view shells and sub shells.
905     2. Set up the internal shell stack.
906     3. Get the SFX shell stack.
907     4. Find the lowest shell in which the two stacks differ.
908     5. Remove all shells above and including that shell from the SFX stack.
909     6. Push all shells of the internal stack on the SFX shell stack that are
910     not already present on the later.
911 */
912 void ViewShellManager::Implementation::UpdateShellStack (void)
913 {
914     ::osl::MutexGuard aGuard (maMutex);
915 
916     // Remember the undo manager from the top-most shell on the stack.
917     SfxShell* pTopMostShell = mrBase.GetSubShell(0);
918     ::svl::IUndoManager* pUndoManager = (pTopMostShell!=NULL)
919         ? pTopMostShell->GetUndoManager()
920         : NULL;
921 
922     // 1. Create the missing shells.
923     CreateShells();
924 
925 
926     // 2. Create the internal target stack.
927     ShellStack aTargetStack;
928     CreateTargetStack(aTargetStack);
929 
930 
931     // 3. Get SFX shell stack.
932     ShellStack aSfxShellStack;
933     sal_uInt16 nIndex (0);
934     while (mrBase.GetSubShell(nIndex)!=NULL)
935         ++nIndex;
936     aSfxShellStack.reserve(nIndex);
937     while (nIndex-- > 0)
938         aSfxShellStack.push_back(mrBase.GetSubShell(nIndex));
939 
940 
941 #ifdef VERBOSE
942     OSL_TRACE("Current SFX Stack\r");
943     DumpShellStack(aSfxShellStack);
944     OSL_TRACE("Target Stack\r");
945     DumpShellStack(aTargetStack);
946 #endif
947 
948 
949     // 4. Find the lowest shell in which the two stacks differ.
950     ShellStack::const_iterator iSfxShell (aSfxShellStack.begin());
951     ShellStack::iterator iTargetShell (aTargetStack.begin());
952     while (iSfxShell != aSfxShellStack.end()
953         && iTargetShell!=aTargetStack.end()
954         && (*iSfxShell)==(*iTargetShell))
955     {
956         ++iSfxShell;
957         ++iTargetShell;
958     }
959 
960 
961     // 5. Remove all shells above and including the differing shell from the
962     // SFX stack starting with the shell on top of the stack.
963     while (iSfxShell != aSfxShellStack.end())
964     {
965         SfxShell* pShell = aSfxShellStack.back();
966         aSfxShellStack.pop_back();
967 #ifdef VERBOSE
968         OSL_TRACE("removing shell %p from stack\r", pShell);
969 #endif
970         mrBase.RemoveSubShell(pShell);
971     }
972 
973 
974     // 6. Push shells from the given stack onto the SFX stack.
975     mbShellStackIsUpToDate = false;
976     while (iTargetShell != aTargetStack.end())
977     {
978 #ifdef VERBOSE
979         OSL_TRACE("pushing shell %p on stack\r", *iTargetShell);
980 #endif
981         mrBase.AddSubShell(**iTargetShell);
982         ++iTargetShell;
983 
984         // The pushing of the shell on to the shell stack may have lead to
985         // another invocation of this method.  In this case we have to abort
986         // pushing shells on the stack and return immediately.
987         if (mbShellStackIsUpToDate)
988             break;
989     }
990     if (mrBase.GetDispatcher() != NULL)
991         mrBase.GetDispatcher()->Flush();
992 
993     // Update the pointer to the top-most shell and set its undo manager
994     // to the one of the previous top-most shell.
995     mpTopShell = mrBase.GetSubShell(0);
996     if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL)
997         mpTopShell->SetUndoManager(pUndoManager);
998 
999     // Finally tell an invocation of this method on a higher level that it can (has
1000     // to) abort and return immediately.
1001     mbShellStackIsUpToDate = true;
1002 
1003 #ifdef VERBOSE
1004     OSL_TRACE("New current stack\r");
1005     DumpSfxShellStack();
1006 #endif
1007 }
1008 
1009 
1010 
1011 
1012 void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell* pShell)
1013 {
1014     ::osl::MutexGuard aGuard (maMutex);
1015 
1016     // Remember the undo manager from the top-most shell on the stack.
1017     SfxShell* pTopMostShell = mrBase.GetSubShell(0);
1018     ::svl::IUndoManager* pUndoManager = (pTopMostShell!=NULL)
1019         ? pTopMostShell->GetUndoManager()
1020         : NULL;
1021 
1022 #ifdef VERBOSE
1023     OSL_TRACE("TakeShellsFromStack(%p)\r", pShell);
1024     DumpSfxShellStack();
1025 #endif
1026 
1027     // 0.Make sure that the given shell is on the stack.  This is a
1028     // preparation for the following assertion.
1029     for (sal_uInt16 nIndex=0; true; nIndex++)
1030     {
1031         SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
1032         if (pShellOnStack == NULL)
1033         {
1034             // Set pShell to NULL to indicate the following code that the
1035             // shell is not on the stack.
1036             pShell = NULL;
1037             break;
1038         }
1039         else if (pShellOnStack == pShell)
1040             break;
1041     }
1042 
1043     if (pShell != NULL)
1044     {
1045         // 1. Deactivate our shells on the stack before they are removed so
1046         // that during the Deactivation() calls the stack is still intact.
1047         for (sal_uInt16 nIndex=0; true; nIndex++)
1048         {
1049             SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
1050             Deactivate(pShellOnStack);
1051             if (pShellOnStack == pShell)
1052                 break;
1053         }
1054 
1055         // 2. Remove the shells from the stack.
1056         while (true)
1057         {
1058             SfxShell* pShellOnStack = mrBase.GetSubShell(0);
1059 #ifdef VERBOSE
1060             OSL_TRACE("removing shell %p from stack\r", pShellOnStack);
1061 #endif
1062             mrBase.RemoveSubShell(pShellOnStack);
1063             if (pShellOnStack == pShell)
1064                 break;
1065         }
1066 
1067         // 3. Update the stack.
1068         if (mrBase.GetDispatcher() != NULL)
1069             mrBase.GetDispatcher()->Flush();
1070 
1071         // Update the pointer to the top-most shell and set its undo manager
1072         // to the one of the previous top-most shell.
1073         mpTopShell = mrBase.GetSubShell(0);
1074         if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL)
1075             mpTopShell->SetUndoManager(pUndoManager);
1076     }
1077 
1078 #ifdef VERBOSE
1079     OSL_TRACE("Sfx shell stack is:\r");
1080     DumpSfxShellStack();
1081 #endif
1082 }
1083 
1084 
1085 
1086 
1087 void ViewShellManager::Implementation::CreateShells (void)
1088 {
1089     ::osl::MutexGuard aGuard (maMutex);
1090 
1091     // Iterate over all view shells.
1092     ShellStack aShellStack;
1093     ActiveShellList::reverse_iterator iShell;
1094     for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell)
1095     {
1096         // Get the list of associated sub shells.
1097         SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell));
1098         if (iList != maActiveSubShells.end())
1099         {
1100             SubShellSubList& rList (iList->second);
1101 
1102             // Iterate over all sub shells of the current view shell.
1103             SubShellSubList::iterator iSubShell;
1104             for (iSubShell=rList.begin(); iSubShell!=rList.end(); ++iSubShell)
1105             {
1106                 if (iSubShell->mpShell == NULL)
1107                 {
1108                     *iSubShell = CreateSubShell(iShell->mpShell,iSubShell->mnId,NULL,NULL);
1109                 }
1110             }
1111         }
1112    }
1113 }
1114 
1115 
1116 
1117 
1118 void ViewShellManager::Implementation::CreateTargetStack (ShellStack& rStack) const
1119 {
1120     // Create a local stack of the shells that are to push on the shell
1121     // stack.  We can thus safly create the required shells wile still
1122     // having a valid shell stack.
1123     for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin());
1124          iViewShell != maActiveViewShells.rend();
1125          ++iViewShell)
1126     {
1127         // Possibly place the form shell below the current view shell.
1128         if ( ! mbFormShellAboveParent
1129             && mpFormShell!=NULL
1130             && iViewShell->mpShell==mpFormShellParent)
1131         {
1132             rStack.push_back(mpFormShell);
1133         }
1134 
1135         // Put the view shell itself on the local stack.
1136         rStack.push_back (iViewShell->mpShell);
1137 
1138         // Possibly place the form shell above the current view shell.
1139         if (mbFormShellAboveParent
1140             && mpFormShell!=NULL
1141             && iViewShell->mpShell==mpFormShellParent)
1142         {
1143             rStack.push_back(mpFormShell);
1144         }
1145 
1146         // Add all other sub shells.
1147         SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell));
1148         if (iList != maActiveSubShells.end())
1149         {
1150             const SubShellSubList& rList (iList->second);
1151             SubShellSubList::const_reverse_iterator iSubShell;
1152             for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell)
1153                 if (iSubShell->mpShell != mpFormShell)
1154                     rStack.push_back(iSubShell->mpShell);
1155         }
1156     }
1157 }
1158 
1159 
1160 
1161 
1162 IMPL_LINK(ViewShellManager::Implementation, WindowEventHandler, VclWindowEvent*, pEvent)
1163 {
1164     if (pEvent != NULL)
1165     {
1166         ::Window* pEventWindow
1167             = static_cast<VclWindowEvent*>(pEvent)->GetWindow();
1168 
1169         switch (pEvent->GetId())
1170         {
1171             case VCLEVENT_WINDOW_GETFOCUS:
1172             {
1173                 for (ActiveShellList::iterator aI(maActiveViewShells.begin());
1174                      aI!=maActiveViewShells.end();
1175                      aI++)
1176                 {
1177                     if (pEventWindow == static_cast< ::Window*>(aI->GetWindow()))
1178                     {
1179                         MoveToTop(*aI->mpShell);
1180                         break;
1181                     }
1182                 }
1183             }
1184             break;
1185 
1186             case VCLEVENT_WINDOW_LOSEFOCUS:
1187                 break;
1188         }
1189     }
1190     return sal_True;
1191 }
1192 
1193 
1194 
1195 
1196 ShellDescriptor ViewShellManager::Implementation::CreateSubShell (
1197     SfxShell* pParentShell,
1198     ShellId nShellId,
1199     ::Window* pParentWindow,
1200     FrameView* pFrameView)
1201 {
1202     ::osl::MutexGuard aGuard (maMutex);
1203     ShellDescriptor aResult;
1204 
1205     // Look up the factories for the parent shell.
1206     ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
1207         maShellFactories.equal_range(pParentShell));
1208 
1209     // Try all factories to create the shell.
1210     for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
1211     {
1212         SharedShellFactory pFactory = iFactory->second;
1213         if (pFactory != NULL)
1214             aResult.mpShell = pFactory->CreateShell(nShellId, pParentWindow, pFrameView);
1215 
1216         // Exit the loop when the shell has been successfully created.
1217         if (aResult.mpShell != NULL)
1218         {
1219             aResult.mpFactory = pFactory;
1220             aResult.mnId = nShellId;
1221             break;
1222         }
1223     }
1224 
1225     return aResult;
1226 }
1227 
1228 
1229 
1230 
1231 void ViewShellManager::Implementation::DestroyViewShell (
1232     const ShellDescriptor& rDescriptor)
1233 {
1234     OSL_ASSERT(rDescriptor.mpShell != NULL);
1235 
1236     ::Window* pWindow = rDescriptor.GetWindow();
1237     if (pWindow != NULL)
1238     {
1239         pWindow->RemoveEventListener(
1240             LINK(this, ViewShellManager::Implementation, WindowEventHandler));
1241     }
1242 
1243     // Destroy the sub shell factories.
1244     ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
1245         maShellFactories.equal_range(rDescriptor.mpShell));
1246     if (aRange.first != maShellFactories.end())
1247         maShellFactories.erase(aRange.first, aRange.second);
1248 
1249     // Release the shell.
1250     if (rDescriptor.mpFactory.get() != NULL)
1251         rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
1252 }
1253 
1254 
1255 
1256 
1257 void ViewShellManager::Implementation::DestroySubShell (
1258     const SfxShell& rParentShell,
1259     const ShellDescriptor& rDescriptor)
1260 {
1261     (void)rParentShell;
1262     OSL_ASSERT(rDescriptor.mpFactory.get() != NULL);
1263     rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
1264 }
1265 
1266 
1267 
1268 
1269 void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell* pParentShell)
1270 {
1271     ::osl::MutexGuard aGuard (maMutex);
1272 
1273     SubShellList::iterator iList (maActiveSubShells.find(pParentShell));
1274     if (iList != maActiveSubShells.end())
1275     {
1276         SubShellSubList& rList (iList->second);
1277         SubShellSubList::iterator iShell;
1278         for (iShell=rList.begin(); iShell!=rList.end(); ++iShell)
1279             if (iShell->mpShell != NULL)
1280                 iShell->mpShell->Invalidate();
1281     }
1282 }
1283 
1284 
1285 
1286 
1287 void ViewShellManager::Implementation::Shutdown (void)
1288 {
1289     ::osl::MutexGuard aGuard (maMutex);
1290 
1291     // Take stacked shells from stack.
1292     if ( ! maActiveViewShells.empty())
1293     {
1294         UpdateLock aLock (*this);
1295 
1296 
1297         while ( ! maActiveViewShells.empty())
1298         {
1299             SfxShell* pShell = maActiveViewShells.front().mpShell;
1300             if (pShell != NULL)
1301             {
1302                 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell);
1303                 if (pViewShell != NULL)
1304                     DeactivateViewShell(*pViewShell);
1305                 else
1306                     DeactivateShell(*pShell);
1307             }
1308             else
1309             {
1310                 DBG_ASSERT(false,
1311                     "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor");
1312                 maActiveViewShells.pop_front();
1313             }
1314         }
1315     }
1316     mrBase.RemoveSubShell (NULL);
1317 
1318     maShellFactories.clear();
1319 }
1320 
1321 
1322 
1323 
1324 #ifdef VERBOSE
1325 void ViewShellManager::Implementation::DumpShellStack (const ShellStack& rStack)
1326 {
1327     ShellStack::const_reverse_iterator iEntry;
1328     for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry)
1329         if (*iEntry != NULL)
1330             OSL_TRACE ("    %p : %s\r",
1331                 *iEntry,
1332                 ::rtl::OUStringToOString((*iEntry)->GetName(),RTL_TEXTENCODING_UTF8).getStr());
1333         else
1334             OSL_TRACE("     null\r");
1335 }
1336 
1337 
1338 
1339 
1340 void ViewShellManager::Implementation::DumpSfxShellStack (void)
1341 {
1342     ShellStack aSfxShellStack;
1343     sal_uInt16 nIndex (0);
1344     while (mrBase.GetSubShell(nIndex)!=NULL)
1345         ++nIndex;
1346     aSfxShellStack.reserve(nIndex);
1347     while (nIndex-- > 0)
1348         aSfxShellStack.push_back(mrBase.GetSubShell(nIndex));
1349     DumpShellStack(aSfxShellStack);
1350 }
1351 #endif
1352 
1353 
1354 
1355 void ViewShellManager::Implementation::Deactivate (SfxShell* pShell)
1356 {
1357     OSL_ASSERT(pShell!=NULL);
1358 
1359     // We have to end a text edit for view shells that are to be taken from
1360     // the shell stack.
1361     ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell);
1362     if (pViewShell != NULL)
1363     {
1364 		sd::View* pView = pViewShell->GetView();
1365         if (pView!=NULL && pView->IsTextEdit())
1366         {
1367             pView->SdrEndTextEdit();
1368             pView->UnmarkAll();
1369             pViewShell->GetViewFrame()->GetDispatcher()->Execute(
1370                 SID_OBJECT_SELECT,
1371                 SFX_CALLMODE_ASYNCHRON);
1372         }
1373     }
1374 
1375     // Now we can deactivate the shell.
1376     pShell->Deactivate(sal_True);
1377 }
1378 
1379 
1380 
1381 
1382 void ViewShellManager::Implementation::SetFormShell (
1383     const ViewShell* pFormShellParent,
1384     FmFormShell* pFormShell,
1385     bool bFormShellAboveParent)
1386 {
1387     ::osl::MutexGuard aGuard (maMutex);
1388 
1389     mpFormShellParent = pFormShellParent;
1390     mpFormShell = pFormShell;
1391     mbFormShellAboveParent = bFormShellAboveParent;
1392 }
1393 
1394 
1395 
1396 
1397 namespace {
1398 
1399 ShellDescriptor::ShellDescriptor (void)
1400     : mpShell(NULL),
1401       mnId(0),
1402       mpFactory()
1403 {
1404 }
1405 
1406 
1407 
1408 
1409 ShellDescriptor::ShellDescriptor (
1410     SfxShell* pShell,
1411     ShellId nId)
1412     : mpShell(pShell),
1413       mnId(nId),
1414       mpFactory()
1415 {
1416 }
1417 
1418 
1419 
1420 
1421 ShellDescriptor::ShellDescriptor (const ShellDescriptor& rDescriptor)
1422         : mpShell(rDescriptor.mpShell),
1423           mnId(rDescriptor.mnId),
1424           mpFactory(rDescriptor.mpFactory)
1425 {
1426 }
1427 
1428 
1429 
1430 
1431 ShellDescriptor& ShellDescriptor::operator= (const ShellDescriptor& rDescriptor)
1432 {
1433     mpShell = rDescriptor.mpShell;
1434     mnId = rDescriptor.mnId;
1435     mpFactory = rDescriptor.mpFactory;
1436     return *this;
1437 }
1438 
1439 
1440 
1441 
1442 bool ShellDescriptor::IsMainViewShell (void) const
1443 {
1444     ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell);
1445     if (pViewShell != NULL)
1446         return pViewShell->IsMainViewShell();
1447     else
1448         return false;
1449 }
1450 
1451 
1452 
1453 
1454 ::Window* ShellDescriptor::GetWindow (void) const
1455 {
1456     ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell);
1457     if (pViewShell != NULL)
1458         return pViewShell->GetActiveWindow();
1459     else
1460         return NULL;
1461 }
1462 
1463 
1464 
1465 } // end of anonymous namespace
1466 
1467 } // end of namespace sd
1468