LibreOffice Module sd (master) 1
ViewShellManager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <ViewShellManager.hxx>
21#include <ViewShell.hxx>
22#include <ViewShellBase.hxx>
23#include <Window.hxx>
24#include <DrawDocShell.hxx>
25
26#include <sal/log.hxx>
27#include <sfx2/dispatch.hxx>
28#include <sfx2/viewfrm.hxx>
29#include <svx/svxids.hrc>
30#include <svx/fmshell.hxx>
31#include <vcl/vclevent.hxx>
32#include <osl/diagnose.h>
33
34#include <iterator>
35#include <list>
36#include <unordered_map>
37
38namespace sd {
39
40namespace {
41
54class ShellDescriptor {
55public:
56 SfxShell* mpShell;
59 bool mbIsListenerAddedToWindow;
60
61 ShellDescriptor ();
62 explicit ShellDescriptor (ShellId nId);
63 vcl::Window* GetWindow() const;
64};
65
69class IsShell
70{
71public:
72 explicit IsShell (const SfxShell* pShell) : mpShell(pShell) {}
73 bool operator() (const ShellDescriptor& rDescriptor)
74 { return rDescriptor.mpShell == mpShell; }
75private:
76 const SfxShell* mpShell;
77};
78
82class IsId
83{
84public:
85 explicit IsId (ShellId nId) : mnId(nId) {}
86 bool operator() (const ShellDescriptor& rDescriptor)
87 { return rDescriptor.mnId == mnId; }
88private:
90};
91
92} // end of anonymous namespace
93
95{
96public:
98 ViewShellBase& rBase);
99 ~Implementation() COVERITY_NOEXCEPT_FALSE;
100
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);
122
126 void TakeShellsFromStack (const SfxShell* pShell);
127
129 {
130 public:
131 explicit UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();}
132 ~UpdateLock() COVERITY_NOEXCEPT_FALSE {mrImpl.UnlockUpdate();}
133 private:
135 };
136
143 void LockUpdate();
144
149 void UnlockUpdate();
150
151private:
153 mutable ::osl::Mutex maMutex;
154
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>
159
164 typedef std::list<ShellDescriptor> ActiveShellList;
166
167 typedef std::list<ShellDescriptor> SubShellSubList;
168 typedef std::unordered_map<const SfxShell*,SubShellSubList,ShellHash> SubShellList;
170
174 typedef ::std::vector<SfxShell*> ShellStack;
175
177
184
188
191
192
193 void UpdateShellStack();
194
195 void CreateShells();
196
200 void CreateTargetStack (ShellStack& rStack) const;
201
202 DECL_LINK(WindowEventHandler, VclWindowEvent&, void);
203
204#if OSL_DEBUG_LEVEL >= 2
205 void DumpShellStack (const ShellStack& rStack);
206 void DumpSfxShellStack();
207#endif
208
214 static void Deactivate (SfxShell* pShell);
215
216 ShellDescriptor CreateSubShell (
217 SfxShell const * pShell,
218 ShellId nShellId);
219 void DestroyViewShell (ShellDescriptor& rDescriptor);
220 static void DestroySubShell (const ShellDescriptor& rDescriptor);
221};
222
223//===== ViewShellManager ======================================================
224
226 : mpImpl(new Implementation(rBase)),
227 mbValid(true)
228{
229}
230
232{
233}
234
236 ViewShell const * pViewShell,
237 const SharedShellFactory& rpFactory)
238{
239 if (mbValid)
240 mpImpl->AddShellFactory(pViewShell, rpFactory);
241}
242
244 ViewShell const * pViewShell,
245 const SharedShellFactory& rpFactory)
246{
247 if (mbValid)
248 mpImpl->RemoveShellFactory(pViewShell, rpFactory);
249}
250
252{
253 if (mbValid)
254 return mpImpl->ActivateViewShell(pViewShell);
255}
256
258{
259 if (mbValid && pShell!=nullptr)
260 mpImpl->DeactivateViewShell(*pShell);
261}
262
264 const ViewShell* pParentShell,
265 FmFormShell* pFormShell,
266 bool bAbove)
267{
268 if (mbValid)
269 mpImpl->SetFormShell(pParentShell,pFormShell,bAbove);
270}
271
273{
274 if (mbValid)
275 mpImpl->ActivateSubShell(rViewShell,nId);
276}
277
279{
280 if (mbValid)
281 mpImpl->DeactivateSubShell(rViewShell,nId);
282}
283
285{
286 if (mbValid)
287 mpImpl->InvalidateAllSubShells(pViewShell);
288}
289
291{
292 if (mbValid && pShell!=nullptr)
293 mpImpl->ActivateShell(*pShell);
294}
295
297{
298 if (mbValid && pShell!=nullptr)
299 mpImpl->DeactivateShell(*pShell);
300}
301
302void ViewShellManager::MoveToTop (const ViewShell& rParentShell)
303{
304 if (mbValid)
305 mpImpl->MoveToTop(rParentShell);
306}
307
309{
310 if (mbValid)
311 return mpImpl->GetShell(nId);
312 else
313 return nullptr;
314}
315
317{
318 if (mbValid)
319 return mpImpl->GetTopShell();
320 else
321 return nullptr;
322}
323
325{
326 if (mbValid)
327 return mpImpl->GetTopViewShell();
328 else
329 return nullptr;
330}
331
333{
334 if (mbValid)
335 {
336 mpImpl->Shutdown();
337 mbValid = false;
338 }
339}
340
342{
343 mpImpl->LockUpdate();
344}
345
347{
348 mpImpl->UnlockUpdate();
349}
350
351//===== ViewShellManager::Implementation ======================================
352
354 ViewShellBase& rBase)
355 : mrBase(rBase),
356 mnUpdateLockCount(0),
357 mbShellStackIsUpToDate(true),
358 mpFormShell(nullptr),
359 mpFormShellParent(nullptr),
360 mbFormShellAboveParent(true),
361 mpTopShell(nullptr),
362 mpTopViewShell(nullptr)
363{}
364
366{
367 Shutdown();
368}
369
371 const SfxShell* pViewShell,
372 const SharedShellFactory& rpFactory)
373{
374 bool bAlreadyAdded (false);
375
376 // Check that the given factory has not already been added.
377 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
378 maShellFactories.equal_range(pViewShell));
379 for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
380 if (iFactory->second == rpFactory)
381 {
382 bAlreadyAdded = true;
383 break;
384 }
385
386 // Add the factory if it is not already present.
387 if ( ! bAlreadyAdded)
388 maShellFactories.emplace(pViewShell, rpFactory);
389}
390
392 const SfxShell* pViewShell,
393 const SharedShellFactory& rpFactory)
394{
395 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
396 maShellFactories.equal_range(pViewShell));
397 for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
398 if (iFactory->second == rpFactory)
399 {
400 maShellFactories.erase(iFactory);
401 break;
402 }
403}
404
406{
407 ::osl::MutexGuard aGuard (maMutex);
408
409 ShellDescriptor aResult;
410 aResult.mpShell = pViewShell;
411
412 // Register as window listener so that the shells of the current
413 // window can be moved to the top of the shell stack.
414 if (aResult.mpShell != nullptr)
415 {
416 vcl::Window* pWindow = aResult.GetWindow();
417 if (pWindow != nullptr)
418 {
419 pWindow->AddEventListener(
420 LINK(this, ViewShellManager::Implementation, WindowEventHandler));
421 aResult.mbIsListenerAddedToWindow = true;
422 }
423 else
424 {
425 SAL_WARN("sd.view",
426 "ViewShellManager::ActivateViewShell: "
427 "new view shell has no active window");
428 }
429 }
430
431 ActivateShell(aResult);
432}
433
435{
436 ::osl::MutexGuard aGuard (maMutex);
437
438 ActiveShellList::iterator iShell (::std::find_if (
439 maActiveViewShells.begin(),
440 maActiveViewShells.end(),
441 IsShell(&rShell)));
442 if (iShell == maActiveViewShells.end())
443 return;
444
445 UpdateLock aLocker (*this);
446
447 ShellDescriptor aDescriptor(*iShell);
448 mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell));
449 maActiveViewShells.erase(iShell);
450 TakeShellsFromStack(aDescriptor.mpShell);
451
452 // Deactivate sub shells.
453 SubShellList::iterator iList (maActiveSubShells.find(&rShell));
454 if (iList != maActiveSubShells.end())
455 {
456 SubShellSubList& rList (iList->second);
457 while ( ! rList.empty())
458 DeactivateSubShell(rShell, rList.front().mnId);
459 }
460
461 DestroyViewShell(aDescriptor);
462}
463
465{
466 ::osl::MutexGuard aGuard (maMutex);
467
468 // Create a new shell or recycle on in the cache.
469 ShellDescriptor aDescriptor;
470 aDescriptor.mpShell = &rShell;
471
472 ActivateShell(aDescriptor);
473}
474
475void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor)
476{
477 // Put shell on top of the active view shells.
478 if (rDescriptor.mpShell != nullptr)
479 {
480 maActiveViewShells.insert( maActiveViewShells.begin(), rDescriptor);
481 }
482}
483
485{
486 ::osl::MutexGuard aGuard (maMutex);
487
488 ActiveShellList::iterator iShell (::std::find_if (
489 maActiveViewShells.begin(),
490 maActiveViewShells.end(),
491 IsShell(&rShell)));
492 if (iShell == maActiveViewShells.end())
493 return;
494
495 UpdateLock aLocker (*this);
496
497 ShellDescriptor aDescriptor(*iShell);
498 mrBase.GetDocShell()->Disconnect(dynamic_cast<ViewShell*>(aDescriptor.mpShell));
499 maActiveViewShells.erase(iShell);
500 TakeShellsFromStack(aDescriptor.mpShell);
501
502 // Deactivate sub shells.
503 SubShellList::iterator iList (maActiveSubShells.find(&rShell));
504 if (iList != maActiveSubShells.end())
505 {
506 SubShellSubList& rList (iList->second);
507 while ( ! rList.empty())
508 DeactivateSubShell(rShell, rList.front().mnId);
509 }
510
511 DestroyViewShell(aDescriptor);
512}
513
515 const SfxShell& rParentShell,
516 ShellId nId)
517{
518 ::osl::MutexGuard aGuard (maMutex);
519
520 // Check that the given view shell is active.
521 if (std::none_of (maActiveViewShells.begin(), maActiveViewShells.end(), IsShell(&rParentShell)))
522 return;
523
524 // Create the sub shell list if it does not yet exist.
525 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
526 if (iList == maActiveSubShells.end())
527 iList = maActiveSubShells.emplace(&rParentShell,SubShellSubList()).first;
528
529 // Do not activate an object bar that is already active. Requesting
530 // this is not exactly an error but may be an indication of one.
531 SubShellSubList& rList (iList->second);
532 if (std::any_of(rList.begin(),rList.end(), IsId(nId)))
533 return;
534
535 // Add just the id of the sub shell. The actual shell is created
536 // later in CreateShells().
537 UpdateLock aLock (*this);
538 rList.emplace_back(nId);
539}
540
542 const SfxShell& rParentShell,
543 ShellId nId)
544{
545 ::osl::MutexGuard aGuard (maMutex);
546
547 // Check that the given view shell is active.
548 SubShellList::iterator iList (maActiveSubShells.find(&rParentShell));
549 if (iList == maActiveSubShells.end())
550 return;
551
552 // Look up the sub shell.
553 SubShellSubList& rList (iList->second);
554 SubShellSubList::iterator iShell (
555 ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
556 if (iShell == rList.end())
557 return;
558 SfxShell* pShell = iShell->mpShell;
559 if (pShell == nullptr)
560 return;
561
562 UpdateLock aLock (*this);
563
564 ShellDescriptor aDescriptor(*iShell);
565 // Remove the sub shell from both the internal structure as well as the
566 // SFX shell stack above and including the sub shell.
567 rList.erase(iShell);
568 TakeShellsFromStack(pShell);
569
570 DestroySubShell(aDescriptor);
571}
572
574{
575 ::osl::MutexGuard aGuard (maMutex);
576
577 // Check that we have access to a dispatcher. If not, then we are
578 // (probably) called while the view shell is still being created or
579 // initialized. Without dispatcher we can not rebuild the shell stack
580 // to move the requested shell to the top. So return right away instead
581 // of making a mess without being able to clean up afterwards.
582 if (mrBase.GetDispatcher() == nullptr)
583 return;
584
585 ActiveShellList::iterator iShell (::std::find_if (
586 maActiveViewShells.begin(),
587 maActiveViewShells.end(),
588 IsShell(&rShell)));
589 bool bMove = true;
590 if (iShell != maActiveViewShells.end())
591 {
592 // Is the shell already at the top of the stack? We have to keep
593 // the case in mind that mbKeepMainViewShellOnTop is true. Shells
594 // that are not the main view shell are placed on the second-to-top
595 // position in this case.
596 if (iShell == maActiveViewShells.begin())
597 {
598 // The shell is at the top position and is either a) the main
599 // view shell or b) another shell but the main view shell is not
600 // kept at the top position. We do not have to move the shell.
601 bMove = false;
602 }
603 }
604 else
605 {
606 // The shell is not on the stack. Therefore it can not be moved.
607 // We could insert it but we don't. Use ActivateViewShell() for
608 // that.
609 bMove = false;
610 }
611
612 // When the shell is not at the right position it is removed from the
613 // internal list of shells and inserted at the correct position.
614 if (bMove)
615 {
616 UpdateLock aLock (*this);
617
618 ShellDescriptor aDescriptor(*iShell);
619
620 TakeShellsFromStack(&rShell);
621 maActiveViewShells.erase(iShell);
622
623 maActiveViewShells.insert(maActiveViewShells.begin(), aDescriptor);
624 }
625}
626
628{
629 ::osl::MutexGuard aGuard (maMutex);
630
631 SfxShell* pShell = nullptr;
632
633 // First search the active view shells.
634 ActiveShellList::const_iterator iShell (
635 ::std::find_if (
636 maActiveViewShells.begin(),
637 maActiveViewShells.end(),
638 IsId(nId)));
639 if (iShell != maActiveViewShells.end())
640 pShell = iShell->mpShell;
641 else
642 {
643 // Now search the active sub shells of every active view shell.
644 for (auto const& activeSubShell : maActiveSubShells)
645 {
646 const SubShellSubList& rList (activeSubShell.second);
647 SubShellSubList::const_iterator iSubShell(
648 ::std::find_if(rList.begin(),rList.end(), IsId(nId)));
649 if (iSubShell != rList.end())
650 {
651 pShell = iSubShell->mpShell;
652 break;
653 }
654 }
655 }
656
657 return pShell;
658}
659
661{
662 OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0));
663 return mpTopShell;
664}
665
667{
668 return mpTopViewShell;
669}
670
672{
673 mnUpdateLockCount++;
674}
675
677{
678 ::osl::MutexGuard aGuard (maMutex);
679
680 mnUpdateLockCount--;
681 if (mnUpdateLockCount < 0)
682 {
683 // This should not happen.
684 OSL_ASSERT (mnUpdateLockCount>=0);
685 mnUpdateLockCount = 0;
686 }
687 if (mnUpdateLockCount == 0)
688 UpdateShellStack();
689}
690
702{
703 ::osl::MutexGuard aGuard (maMutex);
704
705 // Remember the undo manager from the top-most shell on the stack.
706 SfxShell* pTopMostShell = mrBase.GetSubShell(0);
707 SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr)
708 ? pTopMostShell->GetUndoManager()
709 : nullptr;
710
711 // 1. Create the missing shells.
712 CreateShells();
713
714 // Update the pointer to the top-most active view shell.
715 mpTopViewShell = (maActiveViewShells.empty())
716 ? nullptr : maActiveViewShells.begin()->mpShell;
717
718
719 // 2. Create the internal target stack.
720 ShellStack aTargetStack;
721 CreateTargetStack(aTargetStack);
722
723 // 3. Get SFX shell stack.
724 ShellStack aSfxShellStack;
725 sal_uInt16 nIndex (0);
726 while (mrBase.GetSubShell(nIndex)!=nullptr)
727 ++nIndex;
728 aSfxShellStack.reserve(nIndex);
729 while (nIndex-- > 0)
730 aSfxShellStack.push_back(mrBase.GetSubShell(nIndex));
731
732#if OSL_DEBUG_LEVEL >= 2
733 SAL_INFO("sd.view", __func__ << ": Current SFX Stack");
734 DumpShellStack(aSfxShellStack);
735 SAL_INFO("sd.view", __func__ << ": Target Stack");
736 DumpShellStack(aTargetStack);
737#endif
738
739 // 4. Find the lowest shell in which the two stacks differ.
740 auto mismatchIters = std::mismatch(aSfxShellStack.begin(), aSfxShellStack.end(),
741 aTargetStack.begin(), aTargetStack.end());
742 ShellStack::iterator iSfxShell (mismatchIters.first);
743 ShellStack::iterator iTargetShell (mismatchIters.second);
744
745 // 5. Remove all shells above and including the differing shell from the
746 // SFX stack starting with the shell on top of the stack.
747 for (std::reverse_iterator<ShellStack::const_iterator> i(aSfxShellStack.end()), iLast(iSfxShell);
748 i != iLast; ++i)
749 {
750 SfxShell* const pShell = *i;
751 SAL_INFO("sd.view", __func__ << ": removing shell " << pShell << " from stack");
752 mrBase.RemoveSubShell(pShell);
753 }
754 aSfxShellStack.erase(iSfxShell, aSfxShellStack.end());
755
756 // 6. Push shells from the given stack onto the SFX stack.
757 mbShellStackIsUpToDate = false;
758 while (iTargetShell != aTargetStack.end())
759 {
760 SAL_INFO("sd.view", __func__ << ": pushing shell " << *iTargetShell << " on stack");
761 mrBase.AddSubShell(**iTargetShell);
762 ++iTargetShell;
763
764 // The pushing of the shell on to the shell stack may have lead to
765 // another invocation of this method. In this case we have to abort
766 // pushing shells on the stack and return immediately.
767 if (mbShellStackIsUpToDate)
768 break;
769 }
770 if (mrBase.GetDispatcher() != nullptr)
771 mrBase.GetDispatcher()->Flush();
772
773 // Update the pointer to the top-most shell and set its undo manager
774 // to the one of the previous top-most shell.
775 mpTopShell = mrBase.GetSubShell(0);
776 if (mpTopShell!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr)
777 mpTopShell->SetUndoManager(pUndoManager);
778
779 // Finally tell an invocation of this method on a higher level that it can (has
780 // to) abort and return immediately.
781 mbShellStackIsUpToDate = true;
782
783#if OSL_DEBUG_LEVEL >= 2
784 SAL_INFO("sd.view", __func__ << ": New current stack");
785 DumpSfxShellStack();
786#endif
787}
788
790{
791 ::osl::MutexGuard aGuard (maMutex);
792
793 // Remember the undo manager from the top-most shell on the stack.
794 SfxShell* pTopMostShell = mrBase.GetSubShell(0);
795 SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr)
796 ? pTopMostShell->GetUndoManager()
797 : nullptr;
798
799#if OSL_DEBUG_LEVEL >= 2
800 SAL_INFO("sd.view", __func__ << "TakeShellsFromStack( " << pShell << ")");
801 DumpSfxShellStack();
802#endif
803
804 // 0.Make sure that the given shell is on the stack. This is a
805 // preparation for the following assertion.
806 for (sal_uInt16 nIndex=0; true; nIndex++)
807 {
808 SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
809 if (pShellOnStack == nullptr)
810 {
811 // Set pShell to NULL to indicate the following code that the
812 // shell is not on the stack.
813 pShell = nullptr;
814 break;
815 }
816 else if (pShellOnStack == pShell)
817 break;
818 }
819
820 if (pShell == nullptr)
821 return;
822
823 // 1. Deactivate our shells on the stack before they are removed so
824 // that during the Deactivation() calls the stack is still intact.
825 for (sal_uInt16 nIndex=0; true; nIndex++)
826 {
827 SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex);
828 Deactivate(pShellOnStack);
829 if (pShellOnStack == pShell)
830 break;
831 }
832
833 // 2. Remove the shells from the stack.
834 while (true)
835 {
836 SfxShell* pShellOnStack = mrBase.GetSubShell(0);
837 SAL_INFO("sd.view", __func__ << "removing shell " << pShellOnStack << " from stack");
838 mrBase.RemoveSubShell(pShellOnStack);
839 if (pShellOnStack == pShell)
840 break;
841 }
842
843 // 3. Update the stack.
844 if (mrBase.GetDispatcher() != nullptr)
845 mrBase.GetDispatcher()->Flush();
846
847 // Update the pointer to the top-most shell and set its undo manager
848 // to the one of the previous top-most shell.
849 mpTopShell = mrBase.GetSubShell(0);
850 if (mpTopShell!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr)
851 mpTopShell->SetUndoManager(pUndoManager);
852
853#if OSL_DEBUG_LEVEL >= 2
854 SAL_INFO("sd.view", __func__ << "Sfx shell stack is:");
855 DumpSfxShellStack();
856#endif
857}
858
860{
861 ::osl::MutexGuard aGuard (maMutex);
862
863 // Iterate over all view shells.
864 ActiveShellList::reverse_iterator iShell;
865 for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell)
866 {
867 // Get the list of associated sub shells.
868 SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell));
869 if (iList != maActiveSubShells.end())
870 {
871 SubShellSubList& rList (iList->second);
872
873 // Iterate over all sub shells of the current view shell.
874 for (auto & subShell : rList)
875 {
876 if (subShell.mpShell == nullptr)
877 {
878 subShell = CreateSubShell(iShell->mpShell,subShell.mnId);
879 }
880 }
881 }
882 }
883}
884
886{
887 // Create a local stack of the shells that are to push on the shell
888 // stack. We can thus safely create the required shells while still
889 // having a valid shell stack.
890 for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin());
891 iViewShell != maActiveViewShells.rend();
892 ++iViewShell)
893 {
894 // Possibly place the form shell below the current view shell.
895 if ( ! mbFormShellAboveParent
896 && mpFormShell!=nullptr
897 && iViewShell->mpShell==mpFormShellParent)
898 {
899 rStack.push_back(mpFormShell);
900 }
901
902 // Put the view shell itself on the local stack.
903 rStack.push_back (iViewShell->mpShell);
904
905 // Possibly place the form shell above the current view shell.
906 if (mbFormShellAboveParent
907 && mpFormShell!=nullptr
908 && iViewShell->mpShell==mpFormShellParent)
909 {
910 rStack.push_back(mpFormShell);
911 }
912
913 // Add all other sub shells.
914 SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell));
915 if (iList != maActiveSubShells.end())
916 {
917 const SubShellSubList& rList (iList->second);
918 SubShellSubList::const_reverse_iterator iSubShell;
919 for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell)
920 if (iSubShell->mpShell != mpFormShell)
921 rStack.push_back(iSubShell->mpShell);
922 }
923 }
924}
925
927{
928 vcl::Window* pEventWindow = rEvent.GetWindow();
929
930 switch (rEvent.GetId())
931 {
932 case VclEventId::WindowGetFocus:
933 {
934 for (auto const& activeShell : maActiveViewShells)
935 {
936 if (pEventWindow == activeShell.GetWindow())
937 {
938 MoveToTop(*activeShell.mpShell);
939 break;
940 }
941 }
942 }
943 break;
944
945 case VclEventId::WindowLoseFocus:
946 break;
947
948 case VclEventId::ObjectDying:
949 // Remember that we do not have to remove the window
950 // listener for this window.
951 for (auto & activeViewShell : maActiveViewShells)
952 {
953 if (activeViewShell.GetWindow() == pEventWindow)
954 {
955 activeViewShell.mbIsListenerAddedToWindow = false;
956 break;
957 }
958 }
959 break;
960
961 default: break;
962 }
963}
964
966 SfxShell const * pParentShell,
967 ShellId nShellId)
968{
969 ::osl::MutexGuard aGuard (maMutex);
970 ShellDescriptor aResult;
971
972 // Look up the factories for the parent shell.
973 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
974 maShellFactories.equal_range(pParentShell));
975
976 // Try all factories to create the shell.
977 for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory)
978 {
979 SharedShellFactory pFactory = iFactory->second;
980 if (pFactory != nullptr)
981 aResult.mpShell = pFactory->CreateShell(nShellId);
982
983 // Exit the loop when the shell has been successfully created.
984 if (aResult.mpShell != nullptr)
985 {
986 aResult.mpFactory = pFactory;
987 aResult.mnId = nShellId;
988 break;
989 }
990 }
991
992 return aResult;
993}
994
996 ShellDescriptor& rDescriptor)
997{
998 OSL_ASSERT(rDescriptor.mpShell != nullptr);
999
1000 if (rDescriptor.mbIsListenerAddedToWindow)
1001 {
1002 rDescriptor.mbIsListenerAddedToWindow = false;
1003 vcl::Window* pWindow = rDescriptor.GetWindow();
1004 if (pWindow != nullptr)
1005 {
1006 pWindow->RemoveEventListener(
1007 LINK(this, ViewShellManager::Implementation, WindowEventHandler));
1008 }
1009 }
1010
1011 // Destroy the sub shell factories.
1012 ::std::pair<FactoryList::iterator,FactoryList::iterator> aRange(
1013 maShellFactories.equal_range(rDescriptor.mpShell));
1014 if (aRange.first != maShellFactories.end())
1015 maShellFactories.erase(aRange.first, aRange.second);
1016
1017 // Release the shell.
1018 if (rDescriptor.mpFactory)
1019 rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
1020}
1021
1023 const ShellDescriptor& rDescriptor)
1024{
1025 OSL_ASSERT(rDescriptor.mpFactory);
1026 rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell);
1027}
1028
1030{
1031 ::osl::MutexGuard aGuard (maMutex);
1032
1033 SubShellList::iterator iList (maActiveSubShells.find(pParentShell));
1034 if (iList != maActiveSubShells.end())
1035 {
1036 SubShellSubList& rList (iList->second);
1037 for (auto const& shell : rList)
1038 if (shell.mpShell != nullptr)
1039 shell.mpShell->Invalidate();
1040 }
1041}
1042
1044{
1045 ::osl::MutexGuard aGuard (maMutex);
1046
1047 // Take stacked shells from stack.
1048 if ( ! maActiveViewShells.empty())
1049 {
1050 UpdateLock aLock (*this);
1051
1052 while ( ! maActiveViewShells.empty())
1053 {
1054 SfxShell* pShell = maActiveViewShells.front().mpShell;
1055 if (pShell != nullptr)
1056 {
1057 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell);
1058 if (pViewShell != nullptr)
1059 DeactivateViewShell(*pViewShell);
1060 else
1061 DeactivateShell(*pShell);
1062 }
1063 else
1064 {
1065 SAL_WARN("sd.view",
1066 "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor");
1067 maActiveViewShells.pop_front();
1068 }
1069 }
1070 }
1071 mrBase.RemoveSubShell ();
1072
1073 maShellFactories.clear();
1074}
1075
1076#if OSL_DEBUG_LEVEL >= 2
1078{
1079 ShellStack::const_reverse_iterator iEntry;
1080 for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry)
1081 if (*iEntry != NULL)
1082 SAL_INFO("sd.view", __func__ << ": " <<
1083 *iEntry << " : " <<
1084 (*iEntry)->GetName());
1085 else
1086 SAL_INFO("sd.view", __func__ << " null");
1087}
1088
1090{
1091 ShellStack aSfxShellStack;
1092 sal_uInt16 nIndex (0);
1093 while (mrBase.GetSubShell(nIndex)!=NULL)
1094 ++nIndex;
1095 aSfxShellStack.reserve(nIndex);
1096 while (nIndex-- > 0)
1097 aSfxShellStack.push_back(mrBase.GetSubShell(nIndex));
1098 DumpShellStack(aSfxShellStack);
1099}
1100#endif
1101
1103{
1104 OSL_ASSERT(pShell!=nullptr);
1105
1106 // We have to end a text edit for view shells that are to be taken from
1107 // the shell stack.
1108 ViewShell* pViewShell = dynamic_cast<ViewShell*>(pShell);
1109 if (pViewShell != nullptr)
1110 {
1111 sd::View* pView = pViewShell->GetView();
1112 if (pView!=nullptr && pView->IsTextEdit())
1113 {
1114 pView->SdrEndTextEdit();
1115 pView->UnmarkAll();
1116 pViewShell->GetViewFrame()->GetDispatcher()->Execute(
1117 SID_OBJECT_SELECT,
1118 SfxCallMode::ASYNCHRON);
1119 }
1120 }
1121
1122 // Now we can deactivate the shell.
1123 pShell->Deactivate(true);
1124}
1125
1127 const ViewShell* pFormShellParent,
1128 FmFormShell* pFormShell,
1129 bool bFormShellAboveParent)
1130{
1131 ::osl::MutexGuard aGuard (maMutex);
1132
1133 mpFormShellParent = pFormShellParent;
1134 mpFormShell = pFormShell;
1135 mbFormShellAboveParent = bFormShellAboveParent;
1136}
1137
1138namespace {
1139
1140ShellDescriptor::ShellDescriptor()
1141 : mpShell(nullptr),
1143 mbIsListenerAddedToWindow(false)
1144{
1145}
1146
1147ShellDescriptor::ShellDescriptor (
1148 ShellId nId)
1149 : mpShell(nullptr),
1150 mnId(nId),
1151 mbIsListenerAddedToWindow(false)
1152{
1153}
1154
1155vcl::Window* ShellDescriptor::GetWindow() const
1156{
1157 ViewShell* pViewShell = dynamic_cast<ViewShell*>(mpShell);
1158 if (pViewShell != nullptr)
1159 return pViewShell->GetActiveWindow();
1160 else
1161 return nullptr;
1162}
1163
1164} // end of anonymous namespace
1165
1166} // end of namespace sd
1167
1168/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::mutex maMutex
virtual bool IsTextEdit() const final override
void UnmarkAll()
const SfxPoolItem * Execute(sal_uInt16 nSlot, SfxCallMode nCall=SfxCallMode::SLOT, const SfxPoolItem **pArgs=nullptr, sal_uInt16 nModi=0, const SfxPoolItem **pInternalArgs=nullptr)
virtual void Deactivate(bool bMDI)
virtual SfxUndoManager * GetUndoManager()
SfxDispatcher * GetDispatcher()
SfxViewShell descendant that the stacked Draw/Impress shells are based on.
void TakeShellsFromStack(const SfxShell *pShell)
Remove all shells from the SFX stack above and including the given shell.
void AddShellFactory(const SfxShell *pViewShell, const SharedShellFactory &rpFactory)
std::list< ShellDescriptor > ActiveShellList
List of the active view shells.
static void DestroySubShell(const ShellDescriptor &rDescriptor)
std::unordered_map< const SfxShell *, SubShellSubList, ShellHash > SubShellList
void DumpShellStack(const ShellStack &rStack)
void InvalidateAllSubShells(const SfxShell *pParentShell)
DECL_LINK(WindowEventHandler, VclWindowEvent &, void)
void RemoveShellFactory(const SfxShell *pViewShell, const SharedShellFactory &rpFactory)
void SetFormShell(const ViewShell *pViewShell, FmFormShell *pFormShell, bool bAbove)
::std::vector< SfxShell * > ShellStack
In this member we remember what shells we have pushed on the shell stack.
void CreateTargetStack(ShellStack &rStack) const
This method rebuilds the stack of shells that are stacked upon the view shell base.
void DeactivateSubShell(const SfxShell &rParentShell, ShellId nId)
void UpdateShellStack()
Update the SFX shell stack (the portion that is visible to us) so that it matches the internal shell ...
void DeactivateViewShell(const ViewShell &rShell)
void MoveToTop(const SfxShell &rParentShell)
void ActivateViewShell(ViewShell *pViewShell)
void DestroyViewShell(ShellDescriptor &rDescriptor)
std::list< ShellDescriptor > SubShellSubList
void DeactivateShell(const SfxShell &rShell)
~Implementation() COVERITY_NOEXCEPT_FALSE
ShellDescriptor CreateSubShell(SfxShell const *pShell, ShellId nShellId)
std::unordered_multimap< const SfxShell *, SharedShellFactory, ShellHash > FactoryList
void UnlockUpdate()
Allow updates of the shell stack.
void LockUpdate()
Prevent updates of the shell stack.
bool mbShellStackIsUpToDate
The UpdateShellStack() method can be called recursively.
void ActivateSubShell(const SfxShell &rParentShell, ShellId nId)
static void Deactivate(SfxShell *pShell)
To be called before a shell is taken from the SFX shell stack.
SfxShell * GetShell(ShellId nId) const
std::shared_ptr< ShellFactory< SfxShell > > SharedShellFactory
void ActivateShell(SfxShell *pShell)
Activate the given shell which is not a view shell.
std::unique_ptr< ViewShellManager::Implementation, o3tl::default_delete< ViewShellManager::Implementation > > mpImpl
void DeactivateSubShell(const ViewShell &rParentShell, ShellId nId)
Deactivate the specified sub shell.
void AddSubShellFactory(ViewShell const *pViewShell, const SharedShellFactory &rpFactory)
Set the factory for sub shells of the specified view shell.
SfxShell * GetShell(ShellId nId) const
Return the first, i.e.
void DeactivateViewShell(const ViewShell *pShell)
Deactivate the specified shell, i.e.
~ViewShellManager()
Before the destructor is called the method Shutdown() has to have been called.
ViewShellManager(ViewShellBase &rBase)
SfxShell * GetTopViewShell() const
Return the top-most active view shell on the internal shell stack.
void InvalidateAllSubShells(ViewShell const *pViewShell)
Send all sub shells of the specified view shell an Invalidate() call.
void DeactivateShell(const SfxShell *pShell)
Deactivate the specified shell.
void Shutdown()
Tell a ViewShellManager object to prepare to be deleted, i.e.
void ActivateViewShell(ViewShell *pViewShell)
Activate the given view shell.
void RemoveSubShellFactory(ViewShell const *pViewShell, const SharedShellFactory &rpFactory)
void ActivateSubShell(const ViewShell &rParentShell, ShellId nId)
Activate the specified shell as sub shell for the given view shell.
void MoveToTop(const ViewShell &rShell)
Move the specified view shell to the top most position on the stack of view shells in relation to the...
void SetFormShell(const ViewShell *pParentShell, FmFormShell *pFormShell, bool bAbove)
Associate the form shell with a view shell and their relative position.
SfxShell * GetTopShell() const
Return the top-most shell on the SFX shell stack regardless of whether that is a view shell or a sub ...
Base class of the stacked shell hierarchy.
Definition: ViewShell.hxx:92
::sd::View * GetView() const
Definition: ViewShell.hxx:144
SD_DLLPUBLIC SfxViewFrame * GetViewFrame() const
Definition: viewshel.cxx:118
virtual SdrEndTextEditKind SdrEndTextEdit(bool bDontDeleteReally=false) override
ends current text editing
Definition: sdview.cxx:772
void RemoveEventListener(const Link< VclWindowEvent &, void > &rEventListener)
vcl::Window * GetWindow(GetWindowType nType) const
void AddEventListener(const Link< VclWindowEvent &, void > &rEventListener)
sal_uInt16 mnId
sal_Int32 nIndex
void * p
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
return NULL
int i
None
ToolbarId ShellId
IMPL_LINK(SdCharHeightPropertyBox, implMenuSelectHdl, const OUString &, rIdent, void)
sal_Int16 nId
bool mbValid
ToolbarId