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