bump product version to 5.0.4.1
[LibreOffice.git] / sd / source / ui / view / ViewShellManager.cxx
blobff3596f87c6a4e6a38667da76c2d00ef7de970af
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "ViewShellManager.hxx"
21 #include "ViewShell.hxx"
22 #include "ViewShellBase.hxx"
23 #include "Window.hxx"
24 #include "DrawDocShell.hxx"
25 #include "FormShellManager.hxx"
27 #include <sfx2/dispatch.hxx>
28 #include <svx/svxids.hrc>
29 #include <svx/fmshell.hxx>
31 #include <iterator>
32 #include <unordered_map>
34 namespace sd {
36 namespace {
38 /** The ShellDescriptor class is used to shells together with their ids and
39 the factory that was used to create the shell.
41 The shell pointer may be NULL. In that case the shell is created on
42 demand by a factory.
44 The factory pointer may be NULL. In that case the shell pointer is
45 given to the ViewShellManager.
47 Shell pointer and factory pointer can but should not be NULL at the same
48 time.
50 class ShellDescriptor {
51 public:
52 SfxShell* mpShell;
53 ShellId mnId;
54 ViewShellManager::SharedShellFactory mpFactory;
55 bool mbIsListenerAddedToWindow;
57 ShellDescriptor ();
58 ShellDescriptor (SfxShell* pShell, ShellId nId);
59 ShellDescriptor (const ShellDescriptor& rDescriptor);
60 ShellDescriptor& operator= (const ShellDescriptor& rDescriptor);
61 bool IsMainViewShell() const;
62 vcl::Window* GetWindow() const;
65 /** This functor can be used to search for a shell in an STL container when the
66 shell pointer is given.
68 class IsShell : public ::std::unary_function<ShellDescriptor,bool>
70 public:
71 IsShell (const SfxShell* pShell) : mpShell(pShell) {}
72 bool operator() (const ShellDescriptor& rDescriptor)
73 { return rDescriptor.mpShell == mpShell; }
74 private:
75 const SfxShell* mpShell;
78 /** This functor can be used to search for a shell in an STL container when the
79 id of the shell is given.
81 class IsId : public ::std::unary_function<ShellDescriptor,bool>
83 public:
84 IsId (ShellId nId) : mnId(nId) {}
85 bool operator() (const ShellDescriptor& rDescriptor)
86 { return rDescriptor.mnId == mnId; }
87 private:
88 ShellId mnId;
91 } // end of anonymous namespace
93 class ViewShellManager::Implementation
95 public:
96 Implementation (
97 ViewShellManager& rManager,
98 ViewShellBase& rBase);
99 ~Implementation();
101 void AddShellFactory (
102 const SfxShell* pViewShell,
103 const SharedShellFactory& rpFactory);
104 void RemoveShellFactory (
105 const SfxShell* pViewShell,
106 const SharedShellFactory& rpFactory);
107 void ActivateViewShell (
108 ViewShell* pViewShell);
109 void DeactivateViewShell (const ViewShell& rShell);
110 void ActivateShell (SfxShell& rShell);
111 void DeactivateShell (const SfxShell& rShell);
112 void ActivateShell (const ShellDescriptor& rDescriptor);
113 void SetFormShell (const ViewShell* pViewShell, FmFormShell* pFormShell, bool bAbove);
114 void ActivateSubShell (const SfxShell& rParentShell, ShellId nId);
115 void DeactivateSubShell (const SfxShell& rParentShell, ShellId nId);
116 void MoveToTop (const SfxShell& rParentShell);
117 SfxShell* GetShell (ShellId nId) const;
118 SfxShell* GetTopShell() const;
119 SfxShell* GetTopViewShell() const;
120 void Shutdown();
121 void InvalidateAllSubShells (const SfxShell* pParentShell);
123 /** Remove all shells from the SFX stack above and including the given
124 shell.
126 void TakeShellsFromStack (const SfxShell* pShell);
128 class UpdateLock
130 public:
131 UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();}
132 ~UpdateLock() {mrImpl.UnlockUpdate();};
133 private:
134 Implementation& mrImpl;
137 /** Prevent updates of the shell stack. While the sub shell manager is
138 locked it will update its internal data structures but not alter the
139 shell stack. Use this method when there are several modifications
140 to the shell stack to prevent multiple rebuilds of the shell stack
141 and resulting broadcasts.
143 void LockUpdate();
145 /** Allow updates of the shell stack. This method has to be called the
146 same number of times as LockUpdate() to really allow a rebuild of
147 the shell stack.
149 void UnlockUpdate();
151 private:
152 ViewShellBase& mrBase;
153 mutable ::osl::Mutex maMutex;
155 class ShellHash { public: size_t operator()(const SfxShell* p) const { return reinterpret_cast<size_t>(p);} };
156 typedef std::unordered_multimap<const SfxShell*,SharedShellFactory,ShellHash>
157 FactoryList;
158 FactoryList maShellFactories;
160 /** List of the active view shells. In order to create gather all shells
161 to put on the shell stack each view shell in this list is asked for
162 its sub-shells (typically toolbars).
164 typedef std::list<ShellDescriptor> ActiveShellList;
165 ActiveShellList maActiveViewShells;
167 typedef std::list<ShellDescriptor> SubShellSubList;
168 typedef std::unordered_map<const SfxShell*,SubShellSubList,ShellHash> SubShellList;
169 SubShellList maActiveSubShells;
171 /** In this member we remember what shells we have pushed on the shell
172 stack.
174 typedef ::std::vector<SfxShell*> ShellStack;
176 int mnUpdateLockCount;
178 /** When this flag is set then the main view shell is always kept at the
179 top of the shell stack.
181 bool mbKeepMainViewShellOnTop;
183 /** The UpdateShellStack() method can be called recursively. This flag
184 is used to communicate between different levels of invocation: if
185 the stack has been updated in an inner call the outer call can (has
186 to) stop and return immediately.
188 bool mbShellStackIsUpToDate;
190 SfxShell* mpFormShell;
191 const ViewShell* mpFormShellParent;
192 bool mbFormShellAboveParent;
194 SfxShell* mpTopShell;
195 SfxShell* mpTopViewShell;
198 void UpdateShellStack();
200 void CreateShells();
202 /** This method rebuilds the stack of shells that are stacked upon the
203 view shell base.
205 void CreateTargetStack (ShellStack& rStack) const;
207 DECL_LINK(WindowEventHandler, VclWindowEvent*);
209 #if OSL_DEBUG_LEVEL >= 2
210 void DumpShellStack (const ShellStack& rStack);
211 void DumpSfxShellStack();
212 #endif
214 /** To be called before a shell is taken fom the SFX shell stack. This
215 method deactivates an active text editing to avoid problems with
216 undo managers.
217 Afterwards the Deactivate() of the shell is called.
219 static void Deactivate (SfxShell* pShell);
221 ShellDescriptor CreateSubShell (
222 SfxShell* pShell,
223 ShellId nShellId,
224 vcl::Window* pParentWindow,
225 FrameView* pFrameView);
226 void DestroyViewShell (ShellDescriptor& rDescriptor);
227 static void DestroySubShell (
228 const SfxShell& rViewShell,
229 const ShellDescriptor& rDescriptor);
232 //===== ViewShellManager ======================================================
234 ViewShellManager::ViewShellManager (ViewShellBase& rBase)
235 : mpImpl(new Implementation(*this,rBase)),
236 mbValid(true)
240 ViewShellManager::~ViewShellManager()
244 void ViewShellManager::AddSubShellFactory (
245 ViewShell* pViewShell,
246 const SharedShellFactory& rpFactory)
248 if (mbValid)
249 mpImpl->AddShellFactory(pViewShell, rpFactory);
252 void ViewShellManager::RemoveSubShellFactory (
253 ViewShell* pViewShell,
254 const SharedShellFactory& rpFactory)
256 if (mbValid)
257 mpImpl->RemoveShellFactory(pViewShell, rpFactory);
260 void ViewShellManager::ActivateViewShell (ViewShell* pViewShell)
262 if (mbValid)
263 return mpImpl->ActivateViewShell(pViewShell);
266 void ViewShellManager::DeactivateViewShell (const ViewShell* pShell)
268 if (mbValid && pShell!=NULL)
269 mpImpl->DeactivateViewShell(*pShell);
272 void ViewShellManager::SetFormShell (
273 const ViewShell* pParentShell,
274 FmFormShell* pFormShell,
275 bool bAbove)
277 if (mbValid)
278 mpImpl->SetFormShell(pParentShell,pFormShell,bAbove);
281 void ViewShellManager::ActivateSubShell (const ViewShell& rViewShell, ShellId nId)
283 if (mbValid)
284 mpImpl->ActivateSubShell(rViewShell,nId);
287 void ViewShellManager::DeactivateSubShell (const ViewShell& rViewShell, ShellId nId)
289 if (mbValid)
290 mpImpl->DeactivateSubShell(rViewShell,nId);
293 void ViewShellManager::InvalidateAllSubShells (ViewShell* pViewShell)
295 if (mbValid)
296 mpImpl->InvalidateAllSubShells(pViewShell);
299 void ViewShellManager::ActivateShell (SfxShell* pShell)
301 if (mbValid && pShell!=NULL)
302 mpImpl->ActivateShell(*pShell);
305 void ViewShellManager::DeactivateShell (const SfxShell* pShell)
307 if (mbValid && pShell!=NULL)
308 mpImpl->DeactivateShell(*pShell);
311 void ViewShellManager::MoveToTop (const ViewShell& rParentShell)
313 if (mbValid)
314 mpImpl->MoveToTop(rParentShell);
317 SfxShell* ViewShellManager::GetShell (ShellId nId) const
319 if (mbValid)
320 return mpImpl->GetShell(nId);
321 else
322 return NULL;
325 SfxShell* ViewShellManager::GetTopShell() const
327 if (mbValid)
328 return mpImpl->GetTopShell();
329 else
330 return NULL;
333 SfxShell* ViewShellManager::GetTopViewShell() const
335 if (mbValid)
336 return mpImpl->GetTopViewShell();
337 else
338 return NULL;
341 void ViewShellManager::Shutdown()
343 if (mbValid)
345 mpImpl->Shutdown();
346 mbValid = false;
350 void ViewShellManager::LockUpdate()
352 mpImpl->LockUpdate();
355 void ViewShellManager::UnlockUpdate()
357 mpImpl->UnlockUpdate();
360 //===== ViewShellManager::Implementation ======================================
362 ViewShellManager::Implementation::Implementation (
363 ViewShellManager& rManager,
364 ViewShellBase& rBase)
365 : mrBase(rBase),
366 maMutex(),
367 maShellFactories(),
368 maActiveViewShells(),
369 mnUpdateLockCount(0),
370 mbKeepMainViewShellOnTop(false),
371 mbShellStackIsUpToDate(true),
372 mpFormShell(NULL),
373 mpFormShellParent(NULL),
374 mbFormShellAboveParent(true),
375 mpTopShell(NULL),
376 mpTopViewShell(NULL)
378 (void)rManager;
381 ViewShellManager::Implementation::~Implementation()
383 Shutdown();
386 void ViewShellManager::Implementation::AddShellFactory (
387 const SfxShell* pViewShell,
388 const SharedShellFactory& rpFactory)
390 bool bAlreadyAdded (false);
392 // Check that the given factory has not already been added.
393 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
394 maShellFactories.equal_range(pViewShell));
395 for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
396 if (iFactory->second == rpFactory)
398 bAlreadyAdded = true;
399 break;
402 // Add the factory if it is not already present.
403 if ( ! bAlreadyAdded)
404 maShellFactories.insert(FactoryList::value_type(pViewShell, rpFactory));
407 void ViewShellManager::Implementation::RemoveShellFactory (
408 const SfxShell* pViewShell,
409 const SharedShellFactory& rpFactory)
411 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
412 maShellFactories.equal_range(pViewShell));
413 for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
414 if (iFactory->second == rpFactory)
416 maShellFactories.erase(iFactory);
417 break;
421 void ViewShellManager::Implementation::ActivateViewShell (ViewShell* pViewShell)
423 ::osl::MutexGuard aGuard (maMutex);
425 ShellDescriptor aResult;
426 aResult.mpShell = pViewShell;
428 // Register as window listener so that the shells of the current
429 // window can be moved to the top of the shell stack.
430 if (aResult.mpShell != NULL)
432 vcl::Window* pWindow = aResult.GetWindow();
433 if (pWindow != NULL)
435 pWindow->AddEventListener(
436 LINK(this, ViewShellManager::Implementation, WindowEventHandler));
437 aResult.mbIsListenerAddedToWindow = true;
439 else
441 DBG_ASSERT(false,
442 "ViewShellManager::ActivateViewShell: "
443 "new view shell has no active window");
447 ActivateShell(aResult);
450 void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell& rShell)
452 ::osl::MutexGuard aGuard (maMutex);
454 ActiveShellList::iterator iShell (::std::find_if (
455 maActiveViewShells.begin(),
456 maActiveViewShells.end(),
457 IsShell(&rShell)));
458 if (iShell != maActiveViewShells.end())
460 UpdateLock aLocker (*this);
462 ShellDescriptor aDescriptor(*iShell);
463 mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell));
464 maActiveViewShells.erase(iShell);
465 TakeShellsFromStack(aDescriptor.mpShell);
467 // Deactivate sub shells.
468 SubShellList::iterator iList (maActiveSubShells.find(&rShell));
469 if (iList != maActiveSubShells.end())
471 SubShellSubList& rList (iList->second);
472 while ( ! rList.empty())
473 DeactivateSubShell(rShell, rList.front().mnId);
476 DestroyViewShell(aDescriptor);
480 void ViewShellManager::Implementation::ActivateShell (SfxShell& rShell)
482 ::osl::MutexGuard aGuard (maMutex);
484 // Create a new shell or recycle on in the cache.
485 ShellDescriptor aDescriptor;
486 aDescriptor.mpShell = &rShell;
488 ActivateShell(aDescriptor);
491 void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor)
493 // Put shell on top of the active view shells.
494 if (rDescriptor.mpShell != NULL)
496 // Determine where to put the view shell on the stack. By default
497 // it is put on top of the stack. When the view shell of the center
498 // pane is to be kept top most and the new view shell is not
499 // displayed in the center pane then it is inserted at the position
500 // one below the top.
501 ActiveShellList::iterator iInsertPosition (maActiveViewShells.begin());
502 if (iInsertPosition != maActiveViewShells.end()
503 && mbKeepMainViewShellOnTop
504 && ! rDescriptor.IsMainViewShell()
505 && iInsertPosition->IsMainViewShell())
507 ++iInsertPosition;
509 maActiveViewShells.insert(
510 iInsertPosition,
511 rDescriptor);
515 void ViewShellManager::Implementation::DeactivateShell (const SfxShell& rShell)
517 ::osl::MutexGuard aGuard (maMutex);
519 ActiveShellList::iterator iShell (::std::find_if (
520 maActiveViewShells.begin(),
521 maActiveViewShells.end(),
522 IsShell(&rShell)));
523 if (iShell != maActiveViewShells.end())
525 UpdateLock aLocker (*this);
527 ShellDescriptor aDescriptor(*iShell);
528 mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell));
529 maActiveViewShells.erase(iShell);
530 TakeShellsFromStack(aDescriptor.mpShell);
532 // Deactivate sub shells.
533 SubShellList::iterator iList (maActiveSubShells.find(&rShell));
534 if (iList != maActiveSubShells.end())
536 SubShellSubList& rList (iList->second);
537 while ( ! rList.empty())
538 DeactivateSubShell(rShell, rList.front().mnId);
541 DestroyViewShell(aDescriptor);
545 void ViewShellManager::Implementation::ActivateSubShell (
546 const SfxShell& rParentShell,
547 ShellId nId)
549 ::osl::MutexGuard aGuard (maMutex);
551 // Check that the given view shell is active.
552 ActiveShellList::iterator iShell (::std::find_if (
553 maActiveViewShells.begin(),
554 maActiveViewShells.end(),
555 IsShell(&rParentShell)));
556 if (iShell == maActiveViewShells.end())
557 return;
559 // Create the sub shell list if it does not yet exist.
560 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
561 if (iList == maActiveSubShells.end())
562 iList = maActiveSubShells.insert(
563 SubShellList::value_type(&rParentShell,SubShellSubList())).first;
565 // Do not activate an object bar that is already active. Requesting
566 // this is not exactly an error but may be an indication of one.
567 SubShellSubList& rList (iList->second);
568 if (::std::find_if(rList.begin(),rList.end(), IsId(nId)) != rList.end())
569 return;
571 // Add just the id of the sub shell. The actual shell is created
572 // later in CreateShells().
573 UpdateLock aLock (*this);
574 rList.push_back(ShellDescriptor(NULL, nId));
577 void ViewShellManager::Implementation::DeactivateSubShell (
578 const SfxShell& rParentShell,
579 ShellId nId)
581 ::osl::MutexGuard aGuard (maMutex);
583 // Check that the given view shell is active.
584 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
585 if (iList == maActiveSubShells.end())
586 return;
588 // Look up the sub shell.
589 SubShellSubList& rList (iList->second);
590 SubShellSubList::iterator iShell (
591 ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
592 if (iShell == rList.end())
593 return;
594 SfxShell* pShell = iShell->mpShell;
595 if (pShell == NULL)
596 return;
598 UpdateLock aLock (*this);
600 ShellDescriptor aDescriptor(*iShell);
601 // Remove the sub shell from both the internal structure as well as the
602 // SFX shell stack above and including the sub shell.
603 rList.erase(iShell);
604 TakeShellsFromStack(pShell);
606 DestroySubShell(rParentShell, aDescriptor);
609 void ViewShellManager::Implementation::MoveToTop (const SfxShell& rShell)
611 ::osl::MutexGuard aGuard (maMutex);
613 // Check that we have access to a dispatcher. If not, then we are
614 // (probably) called while the view shell is still being created or
615 // initialized. Without dispatcher we can not rebuild the shell stack
616 // to move the requested shell to the top. So return right away instead
617 // of making a mess without being able to clean up afterwards.
618 if (mrBase.GetDispatcher() == NULL)
619 return;
621 ActiveShellList::iterator iShell (::std::find_if (
622 maActiveViewShells.begin(),
623 maActiveViewShells.end(),
624 IsShell(&rShell)));
625 bool bMove = true;
626 if (iShell != maActiveViewShells.end())
628 // Is the shell already at the top of the stack? We have to keep
629 // the case in mind that mbKeepMainViewShellOnTop is true. Shells
630 // that are not the main view shell are placed on the second-to-top
631 // position in this case.
632 if (iShell == maActiveViewShells.begin()
633 && (iShell->IsMainViewShell() || ! mbKeepMainViewShellOnTop))
635 // The shell is at the top position and is either a) the main
636 // view shell or b) another shell but the main view shell is not
637 // kept at the top position. We do not have to move the shell.
638 bMove = false;
640 else if (iShell == ++maActiveViewShells.begin()
641 && ! iShell->IsMainViewShell()
642 && mbKeepMainViewShellOnTop)
644 // The shell is a the second-to-top position, not the main view
645 // shell and the main view shell is kept at the top position.
646 // Therefore we do not have to move the shell.
647 bMove = false;
650 else
652 // The shell is not on the stack. Therefore it can not be moved.
653 // We could insert it but we don't. Use ActivateViewShell() for
654 // that.
655 bMove = false;
658 // When the shell is not at the right position it is removed from the
659 // internal list of shells and inserted at the correct position.
660 if (bMove)
662 UpdateLock aLock (*this);
664 ShellDescriptor aDescriptor(*iShell);
666 TakeShellsFromStack(&rShell);
667 maActiveViewShells.erase(iShell);
669 // Find out whether to insert at the top or one below.
670 ActiveShellList::iterator aInsertPosition (maActiveViewShells.begin());
671 if (mbKeepMainViewShellOnTop && ! aDescriptor.IsMainViewShell())
673 if (maActiveViewShells.back().IsMainViewShell())
674 ++aInsertPosition;
677 maActiveViewShells.insert(aInsertPosition, aDescriptor);
681 SfxShell* ViewShellManager::Implementation::GetShell (ShellId nId) const
683 ::osl::MutexGuard aGuard (maMutex);
685 SfxShell* pShell = NULL;
687 // First search the active view shells.
688 ActiveShellList::const_iterator iShell (
689 ::std::find_if (
690 maActiveViewShells.begin(),
691 maActiveViewShells.end(),
692 IsId(nId)));
693 if (iShell != maActiveViewShells.end())
694 pShell = iShell->mpShell;
695 else
697 // Now search the active sub shells of every active view shell.
698 SubShellList::const_iterator iList;
699 for (iList=maActiveSubShells.begin(); iList!=maActiveSubShells.end(); ++iList)
701 const SubShellSubList& rList (iList->second);
702 SubShellSubList::const_iterator iSubShell(
703 ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
704 if (iSubShell != rList.end())
706 pShell = iSubShell->mpShell;
707 break;
712 return pShell;
715 SfxShell* ViewShellManager::Implementation::GetTopShell() const
717 OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0));
718 return mpTopShell;
721 SfxShell* ViewShellManager::Implementation::GetTopViewShell() const
723 return mpTopViewShell;
726 void ViewShellManager::Implementation::LockUpdate()
728 mnUpdateLockCount++;
731 void ViewShellManager::Implementation::UnlockUpdate()
733 ::osl::MutexGuard aGuard (maMutex);
735 mnUpdateLockCount--;
736 if (mnUpdateLockCount < 0)
738 // This should not happen.
739 OSL_ASSERT (mnUpdateLockCount>=0);
740 mnUpdateLockCount = 0;
742 if (mnUpdateLockCount == 0)
743 UpdateShellStack();
746 /** Update the SFX shell stack (the portion that is visible to us) so that
747 it matches the internal shell stack. This is done in six steps:
748 1. Create the missing view shells and sub shells.
749 2. Set up the internal shell stack.
750 3. Get the SFX shell stack.
751 4. Find the lowest shell in which the two stacks differ.
752 5. Remove all shells above and including that shell from the SFX stack.
753 6. Push all shells of the internal stack on the SFX shell stack that are
754 not already present on the later.
756 void ViewShellManager::Implementation::UpdateShellStack()
758 ::osl::MutexGuard aGuard (maMutex);
760 // Remember the undo manager from the top-most shell on the stack.
761 SfxShell* pTopMostShell = mrBase.GetSubShell(0);
762 ::svl::IUndoManager* pUndoManager = (pTopMostShell!=NULL)
763 ? pTopMostShell->GetUndoManager()
764 : NULL;
766 // 1. Create the missing shells.
767 CreateShells();
769 // Update the pointer to the top-most active view shell.
770 mpTopViewShell = (maActiveViewShells.empty())
771 ? 0 : maActiveViewShells.begin()->mpShell;
774 // 2. Create the internal target stack.
775 ShellStack aTargetStack;
776 CreateTargetStack(aTargetStack);
778 // 3. Get SFX shell stack.
779 ShellStack aSfxShellStack;
780 sal_uInt16 nIndex (0);
781 while (mrBase.GetSubShell(nIndex)!=NULL)
782 ++nIndex;
783 aSfxShellStack.reserve(nIndex);
784 while (nIndex-- > 0)
785 aSfxShellStack.push_back(mrBase.GetSubShell(nIndex));
787 #if OSL_DEBUG_LEVEL >= 2
788 SAL_INFO("sd.view", OSL_THIS_FUNC << ": Current SFX Stack");
789 DumpShellStack(aSfxShellStack);
790 SAL_INFO("sd.view", OSL_THIS_FUNC << ": Target Stack");
791 DumpShellStack(aTargetStack);
792 #endif
794 // 4. Find the lowest shell in which the two stacks differ.
795 ShellStack::iterator iSfxShell (aSfxShellStack.begin());
796 ShellStack::iterator iTargetShell (aTargetStack.begin());
797 while (iSfxShell != aSfxShellStack.end()
798 && iTargetShell!=aTargetStack.end()
799 && (*iSfxShell)==(*iTargetShell))
801 ++iSfxShell;
802 ++iTargetShell;
805 // 5. Remove all shells above and including the differing shell from the
806 // SFX stack starting with the shell on top of the stack.
807 for (std::reverse_iterator<ShellStack::const_iterator> i(aSfxShellStack.end()), iLast(iSfxShell);
808 i != iLast; ++i)
810 SfxShell* const pShell = *i;
811 SAL_INFO("sd.view", OSL_THIS_FUNC << ": removing shell " << pShell << " from stack");
812 mrBase.RemoveSubShell(pShell);
814 aSfxShellStack.erase(iSfxShell, aSfxShellStack.end());
816 // 6. Push shells from the given stack onto the SFX stack.
817 mbShellStackIsUpToDate = false;
818 while (iTargetShell != aTargetStack.end())
820 SAL_INFO("sd.view", OSL_THIS_FUNC << ": pushing shell " << *iTargetShell << " on stack");
821 mrBase.AddSubShell(**iTargetShell);
822 ++iTargetShell;
824 // The pushing of the shell on to the shell stack may have lead to
825 // another invocation of this method. In this case we have to abort
826 // pushing shells on the stack and return immediately.
827 if (mbShellStackIsUpToDate)
828 break;
830 if (mrBase.GetDispatcher() != NULL)
831 mrBase.GetDispatcher()->Flush();
833 // Update the pointer to the top-most shell and set its undo manager
834 // to the one of the previous top-most shell.
835 mpTopShell = mrBase.GetSubShell(0);
836 if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL)
837 mpTopShell->SetUndoManager(pUndoManager);
839 // Finally tell an invocation of this method on a higher level that it can (has
840 // to) abort and return immediately.
841 mbShellStackIsUpToDate = true;
843 #if OSL_DEBUG_LEVEL >= 2
844 SAL_INFO("sd.view", OSL_THIS_FUNC << ": New current stack");
845 DumpSfxShellStack();
846 #endif
849 void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell* pShell)
851 ::osl::MutexGuard aGuard (maMutex);
853 // Remember the undo manager from the top-most shell on the stack.
854 SfxShell* pTopMostShell = mrBase.GetSubShell(0);
855 ::svl::IUndoManager* pUndoManager = (pTopMostShell!=NULL)
856 ? pTopMostShell->GetUndoManager()
857 : NULL;
859 #if OSL_DEBUG_LEVEL >= 2
860 SAL_INFO("sd.view", OSL_THIS_FUNC << "TakeShellsFromStack( " << pShell << ")");
861 DumpSfxShellStack();
862 #endif
864 // 0.Make sure that the given shell is on the stack. This is a
865 // preparation for the following assertion.
866 for (sal_uInt16 nIndex=0; true; nIndex++)
868 SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
869 if (pShellOnStack == NULL)
871 // Set pShell to NULL to indicate the following code that the
872 // shell is not on the stack.
873 pShell = NULL;
874 break;
876 else if (pShellOnStack == pShell)
877 break;
880 if (pShell != NULL)
882 // 1. Deactivate our shells on the stack before they are removed so
883 // that during the Deactivation() calls the stack is still intact.
884 for (sal_uInt16 nIndex=0; true; nIndex++)
886 SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
887 Deactivate(pShellOnStack);
888 if (pShellOnStack == pShell)
889 break;
892 // 2. Remove the shells from the stack.
893 while (true)
895 SfxShell* pShellOnStack = mrBase.GetSubShell(0);
896 SAL_INFO("sd.view", OSL_THIS_FUNC << "removing shell " << pShellOnStack << " from stack");
897 mrBase.RemoveSubShell(pShellOnStack);
898 if (pShellOnStack == pShell)
899 break;
902 // 3. Update the stack.
903 if (mrBase.GetDispatcher() != NULL)
904 mrBase.GetDispatcher()->Flush();
906 // Update the pointer to the top-most shell and set its undo manager
907 // to the one of the previous top-most shell.
908 mpTopShell = mrBase.GetSubShell(0);
909 if (mpTopShell!=NULL && pUndoManager!=NULL && mpTopShell->GetUndoManager()==NULL)
910 mpTopShell->SetUndoManager(pUndoManager);
913 #if OSL_DEBUG_LEVEL >= 2
914 SAL_INFO("sd.view", OSL_THIS_FUNC << "Sfx shell stack is:");
915 DumpSfxShellStack();
916 #endif
919 void ViewShellManager::Implementation::CreateShells()
921 ::osl::MutexGuard aGuard (maMutex);
923 // Iterate over all view shells.
924 ActiveShellList::reverse_iterator iShell;
925 for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell)
927 // Get the list of associated sub shells.
928 SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell));
929 if (iList != maActiveSubShells.end())
931 SubShellSubList& rList (iList->second);
933 // Iterate over all sub shells of the current view shell.
934 SubShellSubList::iterator iSubShell;
935 for (iSubShell=rList.begin(); iSubShell!=rList.end(); ++iSubShell)
937 if (iSubShell->mpShell == NULL)
939 *iSubShell = CreateSubShell(iShell->mpShell,iSubShell->mnId,NULL,NULL);
946 void ViewShellManager::Implementation::CreateTargetStack (ShellStack& rStack) const
948 // Create a local stack of the shells that are to push on the shell
949 // stack. We can thus safly create the required shells wile still
950 // having a valid shell stack.
951 for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin());
952 iViewShell != maActiveViewShells.rend();
953 ++iViewShell)
955 // Possibly place the form shell below the current view shell.
956 if ( ! mbFormShellAboveParent
957 && mpFormShell!=NULL
958 && iViewShell->mpShell==mpFormShellParent)
960 rStack.push_back(mpFormShell);
963 // Put the view shell itself on the local stack.
964 rStack.push_back (iViewShell->mpShell);
966 // Possibly place the form shell above the current view shell.
967 if (mbFormShellAboveParent
968 && mpFormShell!=NULL
969 && iViewShell->mpShell==mpFormShellParent)
971 rStack.push_back(mpFormShell);
974 // Add all other sub shells.
975 SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell));
976 if (iList != maActiveSubShells.end())
978 const SubShellSubList& rList (iList->second);
979 SubShellSubList::const_reverse_iterator iSubShell;
980 for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell)
981 if (iSubShell->mpShell != mpFormShell)
982 rStack.push_back(iSubShell->mpShell);
987 IMPL_LINK(ViewShellManager::Implementation, WindowEventHandler, VclWindowEvent*, pEvent)
989 if (pEvent != NULL)
991 vcl::Window* pEventWindow
992 = static_cast<VclWindowEvent*>(pEvent)->GetWindow();
994 switch (pEvent->GetId())
996 case VCLEVENT_WINDOW_GETFOCUS:
998 for (ActiveShellList::iterator aI(maActiveViewShells.begin());
999 aI!=maActiveViewShells.end();
1000 ++aI)
1002 if (pEventWindow == static_cast< vcl::Window*>(aI->GetWindow()))
1004 MoveToTop(*aI->mpShell);
1005 break;
1009 break;
1011 case VCLEVENT_WINDOW_LOSEFOCUS:
1012 break;
1014 case VCLEVENT_OBJECT_DYING:
1015 // Remember that we do not have to remove the window
1016 // listener for this window.
1017 for (ActiveShellList::iterator
1018 iShell(maActiveViewShells.begin()),
1019 iEnd(maActiveViewShells.end());
1020 iShell!=iEnd;
1021 ++iShell)
1023 if (iShell->GetWindow() == pEventWindow)
1025 iShell->mbIsListenerAddedToWindow = false;
1026 break;
1029 break;
1032 return sal_IntPtr(true);
1035 ShellDescriptor ViewShellManager::Implementation::CreateSubShell (
1036 SfxShell* pParentShell,
1037 ShellId nShellId,
1038 vcl::Window* pParentWindow,
1039 FrameView* pFrameView)
1041 ::osl::MutexGuard aGuard (maMutex);
1042 ShellDescriptor aResult;
1044 // Look up the factories for the parent shell.
1045 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
1046 maShellFactories.equal_range(pParentShell));
1048 // Try all factories to create the shell.
1049 for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
1051 SharedShellFactory pFactory = iFactory->second;
1052 if (pFactory != 0)
1053 aResult.mpShell = pFactory->CreateShell(nShellId, pParentWindow, pFrameView);
1055 // Exit the loop when the shell has been successfully created.
1056 if (aResult.mpShell != NULL)
1058 aResult.mpFactory = pFactory;
1059 aResult.mnId = nShellId;
1060 break;
1064 return aResult;
1067 void ViewShellManager::Implementation::DestroyViewShell (
1068 ShellDescriptor& rDescriptor)
1070 OSL_ASSERT(rDescriptor.mpShell != NULL);
1072 if (rDescriptor.mbIsListenerAddedToWindow)
1074 rDescriptor.mbIsListenerAddedToWindow = false;
1075 vcl::Window* pWindow = rDescriptor.GetWindow();
1076 if (pWindow != NULL)
1078 pWindow->RemoveEventListener(
1079 LINK(this, ViewShellManager::Implementation, WindowEventHandler));
1083 // Destroy the sub shell factories.
1084 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
1085 maShellFactories.equal_range(rDescriptor.mpShell));
1086 if (aRange.first != maShellFactories.end())
1087 maShellFactories.erase(aRange.first, aRange.second);
1089 // Release the shell.
1090 if (rDescriptor.mpFactory.get() != NULL)
1091 rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
1094 void ViewShellManager::Implementation::DestroySubShell (
1095 const SfxShell& rParentShell,
1096 const ShellDescriptor& rDescriptor)
1098 (void)rParentShell;
1099 OSL_ASSERT(rDescriptor.mpFactory.get() != NULL);
1100 rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
1103 void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell* pParentShell)
1105 ::osl::MutexGuard aGuard (maMutex);
1107 SubShellList::iterator iList (maActiveSubShells.find(pParentShell));
1108 if (iList != maActiveSubShells.end())
1110 SubShellSubList& rList (iList->second);
1111 SubShellSubList::iterator iShell;
1112 for (iShell=rList.begin(); iShell!=rList.end(); ++iShell)
1113 if (iShell->mpShell != NULL)
1114 iShell->mpShell->Invalidate();
1118 void ViewShellManager::Implementation::Shutdown()
1120 ::osl::MutexGuard aGuard (maMutex);
1122 // Take stacked shells from stack.
1123 if ( ! maActiveViewShells.empty())
1125 UpdateLock aLock (*this);
1127 while ( ! maActiveViewShells.empty())
1129 SfxShell* pShell = maActiveViewShells.front().mpShell;
1130 if (pShell != NULL)
1132 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell);
1133 if (pViewShell != NULL)
1134 DeactivateViewShell(*pViewShell);
1135 else
1136 DeactivateShell(*pShell);
1138 else
1140 DBG_ASSERT(false,
1141 "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor");
1142 maActiveViewShells.pop_front();
1146 mrBase.RemoveSubShell (NULL);
1148 maShellFactories.clear();
1151 #if OSL_DEBUG_LEVEL >= 2
1152 void ViewShellManager::Implementation::DumpShellStack (const ShellStack& rStack)
1154 ShellStack::const_reverse_iterator iEntry;
1155 for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry)
1156 if (*iEntry != NULL)
1157 SAL_INFO("sd.view", OSL_THIS_FUNC << ": " <<
1158 *iEntry << " : " <<
1159 OUStringToOString((*iEntry)->GetName(),RTL_TEXTENCODING_UTF8).getStr());
1160 else
1161 SAL_INFO("sd.view", OSL_THIS_FUNC << " null");
1164 void ViewShellManager::Implementation::DumpSfxShellStack()
1166 ShellStack aSfxShellStack;
1167 sal_uInt16 nIndex (0);
1168 while (mrBase.GetSubShell(nIndex)!=NULL)
1169 ++nIndex;
1170 aSfxShellStack.reserve(nIndex);
1171 while (nIndex-- > 0)
1172 aSfxShellStack.push_back(mrBase.GetSubShell(nIndex));
1173 DumpShellStack(aSfxShellStack);
1175 #endif
1177 void ViewShellManager::Implementation::Deactivate (SfxShell* pShell)
1179 OSL_ASSERT(pShell!=NULL);
1181 // We have to end a text edit for view shells that are to be taken from
1182 // the shell stack.
1183 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell);
1184 if (pViewShell != NULL)
1186 sd::View* pView = pViewShell->GetView();
1187 if (pView!=NULL && pView->IsTextEdit())
1189 pView->SdrEndTextEdit();
1190 pView->UnmarkAll();
1191 pViewShell->GetViewFrame()->GetDispatcher()->Execute(
1192 SID_OBJECT_SELECT,
1193 SfxCallMode::ASYNCHRON);
1197 // Now we can deactivate the shell.
1198 pShell->Deactivate(true);
1201 void ViewShellManager::Implementation::SetFormShell (
1202 const ViewShell* pFormShellParent,
1203 FmFormShell* pFormShell,
1204 bool bFormShellAboveParent)
1206 ::osl::MutexGuard aGuard (maMutex);
1208 mpFormShellParent = pFormShellParent;
1209 mpFormShell = pFormShell;
1210 mbFormShellAboveParent = bFormShellAboveParent;
1213 namespace {
1215 ShellDescriptor::ShellDescriptor()
1216 : mpShell(NULL),
1217 mnId(0),
1218 mpFactory(),
1219 mbIsListenerAddedToWindow(false)
1223 ShellDescriptor::ShellDescriptor (
1224 SfxShell* pShell,
1225 ShellId nId)
1226 : mpShell(pShell),
1227 mnId(nId),
1228 mpFactory(),
1229 mbIsListenerAddedToWindow(false)
1233 ShellDescriptor::ShellDescriptor (const ShellDescriptor& rDescriptor)
1234 : mpShell(rDescriptor.mpShell),
1235 mnId(rDescriptor.mnId),
1236 mpFactory(rDescriptor.mpFactory),
1237 mbIsListenerAddedToWindow(rDescriptor.mbIsListenerAddedToWindow)
1241 ShellDescriptor& ShellDescriptor::operator= (const ShellDescriptor& rDescriptor)
1243 if (this != &rDescriptor)
1245 mpShell = rDescriptor.mpShell;
1246 mnId = rDescriptor.mnId;
1247 mpFactory = rDescriptor.mpFactory;
1248 mbIsListenerAddedToWindow = rDescriptor.mbIsListenerAddedToWindow;
1250 return *this;
1253 bool ShellDescriptor::IsMainViewShell() const
1255 ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell);
1256 if (pViewShell != NULL)
1257 return pViewShell->IsMainViewShell();
1258 else
1259 return false;
1262 vcl::Window* ShellDescriptor::GetWindow() const
1264 ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell);
1265 if (pViewShell != NULL)
1266 return pViewShell->GetActiveWindow();
1267 else
1268 return NULL;
1271 } // end of anonymous namespace
1273 } // end of namespace sd
1275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */