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