diff options
Diffstat (limited to 'sd/source/ui/view/ToolBarManager.cxx')
-rw-r--r-- | sd/source/ui/view/ToolBarManager.cxx | 1375 |
1 files changed, 1375 insertions, 0 deletions
diff --git a/sd/source/ui/view/ToolBarManager.cxx b/sd/source/ui/view/ToolBarManager.cxx new file mode 100644 index 000000000..0bb0f9528 --- /dev/null +++ b/sd/source/ui/view/ToolBarManager.cxx @@ -0,0 +1,1375 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <ToolBarManager.hxx> + +#include <DrawViewShell.hxx> +#include <EventMultiplexer.hxx> +#include <ViewShellBase.hxx> +#include <ViewShellManager.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> + +#include <sal/log.hxx> +#include <osl/mutex.hxx> +#include <o3tl/deleter.hxx> +#include <o3tl/enumrange.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/toolbarids.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svx/svxids.hrc> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <tools/debug.hxx> +#include <tools/link.hxx> +#include <vcl/svapp.hxx> +#include <osl/diagnose.h> + +#include <map> +#include <utility> +#include <memory> +#include <string_view> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +using namespace sd; + +class ToolBarRules; + +/** Lock of the frame::XLayoutManager. +*/ +class LayouterLock +{ + Reference<frame::XLayoutManager> mxLayouter; +public: + explicit LayouterLock (const Reference<frame::XLayoutManager>& rxLayouter); + ~LayouterLock(); + bool is() const { return mxLayouter.is(); } +}; + +/** Store a list of tool bars for each of the tool bar groups. From + this the list of requested tool bars is built. +*/ +class ToolBarList +{ +public: + ToolBarList(); + + void ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup); + void AddToolBar (sd::ToolBarManager::ToolBarGroup eGroup, const OUString& rsName); + bool RemoveToolBar (sd::ToolBarManager::ToolBarGroup eGroup, const OUString& rsName); + + void GetToolBarsToActivate (std::vector<OUString>& rToolBars) const; + void GetToolBarsToDeactivate (std::vector<OUString>& rToolBars) const; + + void MarkToolBarAsActive (const OUString& rsName); + void MarkToolBarAsNotActive (const OUString& rsName); + void MarkAllToolBarsAsNotActive(); + +private: + typedef ::std::map<sd::ToolBarManager::ToolBarGroup, std::vector<OUString> > Groups; + Groups maGroups; + std::vector<OUString> maActiveToolBars; + + void MakeRequestedToolBarList (std::vector<OUString>& rToolBars) const; +}; + +/** Manage tool bars that are implemented as sub shells of a view shell. + The typical procedure of updating the sub shells of a view shell is to + rebuild a list of sub shells that the caller would like to have active. + The methods ClearGroup() and AddShellId() allow the caller to do that. A + final call to UpdateShells() activates the requested shells that are not + active and deactivates the active shells that are not requested . + + This is done by maintaining two lists. One (the current list) + reflects the current state. The other (the requested list) contains the + currently requested shells. UpdateShells() makes the requested + list the current list and clears the current list. + + Each shell belongs to one group. Different groups can be modified + separately. +*/ +class ToolBarShellList +{ +public: + /** Create a new object with an empty current list and an empty + requested list. + */ + ToolBarShellList(); + + /** Remove all shells from a group. Calling this method should normally + not be necessary because after the construction or after a call to + UpdateShells() the requested list is empty. + @param eGroup + The group to clear. Shells in other groups are not modified. + */ + void ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup); + + /** Add a shell. When the specified shell has already been requested + for another group then it is moved to this group. + @param eGroup + The group to which to add the shell. + @param nId + The id of the shell to add. + */ + void AddShellId (sd::ToolBarManager::ToolBarGroup eGroup, sd::ShellId nId); + + /** Releasing all shells means that the given ToolBarRules object is + informed that every shell managed by the called ToolBarShellList is + about to be removed and that the associated framework tool bars can + be removed as well. The caller still has to call UpdateShells(). + */ + void ReleaseAllShells (ToolBarRules& rRules); + + /** The requested list is made the current list by activating all + shells in the requested list and by deactivating the shells in the + current list that are not in the requested list. + @param pMainViewShell + The shells that are activated or deactivated are sub shells of + this view shell. + @param rManager + This ViewShellManager is used to activate or deactivate shells. + */ + void UpdateShells ( + const std::shared_ptr<ViewShell>& rpMainViewShell, + const std::shared_ptr<ViewShellManager>& rpManager); + +private: + class ShellDescriptor + {public: + ShellDescriptor (ShellId nId,sd::ToolBarManager::ToolBarGroup eGroup); + ShellId mnId; + sd::ToolBarManager::ToolBarGroup meGroup; + friend bool operator<(const ShellDescriptor& r1, const ShellDescriptor& r2) + { return r1.mnId < r2.mnId; } + }; + + /** The requested list of tool bar shells that will be active after the + next call to UpdateShells(). + */ + typedef ::std::set<ShellDescriptor> GroupedShellList; + GroupedShellList maNewList; + + /** The list of tool bar shells that are currently on the shell stack. + Using a GroupedShellList is not strictly necessary but it makes + things easier and does not waste too much memory. + */ + GroupedShellList maCurrentList; +}; + +/** This class concentrates the knowledge about when to show what tool bars + in one place. +*/ +class ToolBarRules +{ +public: + ToolBarRules ( + const std::shared_ptr<ToolBarManager>& rpToolBarManager, + const std::shared_ptr<ViewShellManager>& rpViewShellManager); + + /** This method calls MainViewShellChanged() and SelectionHasChanged() + for the current main view shell and its view. + */ + void Update (ViewShellBase const & rBase); + + /** Reset all tool bars in all groups and add tool bars and tool bar + shells to the ToolBarGroup::Permanent group for the specified ViewShell type. + */ + void MainViewShellChanged (ViewShell::ShellType nShellType); + + /** Reset all tool bars in all groups and add tool bars and tool bar + shells to the ToolBarGroup::Permanent group for the specified ViewShell. + */ + void MainViewShellChanged (const ViewShell& rMainViewShell); + + /** Reset all tool bars in the ToolBarGroup::Function group and add tool bars and tool bar + shells to this group for the current selection. + */ + void SelectionHasChanged ( + const ::sd::ViewShell& rViewShell, + const SdrView& rView); + + /** Add a tool bar for the specified tool bar shell. + */ + void SubShellAdded ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId); + + /** Remove a tool bar for the specified tool bar shell. + */ + void SubShellRemoved ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId); + +private: + std::shared_ptr<ToolBarManager> mpToolBarManager; + std::shared_ptr<ViewShellManager> mpViewShellManager; +}; + +} // end of anonymous namespace + +namespace sd { + +//===== ToolBarManager::Implementation ======================================== + +class ToolBarManager::Implementation +{ +public: + /** This constructor takes three arguments even though the + ToolBarManager could be taken from the ViewShellBase. This is so to + state explicitly which information has to be present when this + constructor is called. The ViewShellBase may not have been fully + initialized at this point and must not be asked for this values. + */ + Implementation ( + ViewShellBase& rBase, + const std::shared_ptr<sd::tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager, + const std::shared_ptr<ToolBarManager>& rpToolBarManager); + ~Implementation(); + + void SetValid (bool bValid); + + void ResetToolBars (ToolBarGroup eGroup); + void ResetAllToolBars(); + void AddToolBar (ToolBarGroup eGroup, const OUString& rsToolBarName); + void AddToolBarShell (ToolBarGroup eGroup, ShellId nToolBarId); + void RemoveToolBar (ToolBarGroup eGroup, const OUString& rsToolBarName); + + /** Release all tool bar shells and the associated framework tool bars. + Typically called when the main view shell is being replaced by + another, all tool bar shells are released. In that process the + shells are destroyed anyway and without calling this method they + would still be referenced. + */ + void ReleaseAllToolBarShells(); + + void ToolBarsDestroyed(); + + void RequestUpdate(); + + void PreUpdate(); + void PostUpdate(); + /** Tell the XLayoutManager about the tool bars that we would like to be + shown. + @param rpLayouterLock + This typically is the mpSynchronousLayouterLock that is used in + this method and that is either released at its end or assigned + to mpAsynchronousLock in order to be unlocked later. + */ + void Update (::std::unique_ptr<LayouterLock> pLayouterLock); + + class UpdateLockImplementation + { + public: + explicit UpdateLockImplementation (Implementation& rImplementation) + : mrImplementation(rImplementation) { mrImplementation.LockUpdate(); } + ~UpdateLockImplementation() { mrImplementation.UnlockUpdate(); } + private: + Implementation& mrImplementation; + }; + + void LockViewShellManager(); + void LockUpdate(); + void UnlockUpdate(); + + ToolBarRules& GetToolBarRules() { return maToolBarRules;} + +private: + mutable ::osl::Mutex maMutex; + ViewShellBase& mrBase; + std::shared_ptr<sd::tools::EventMultiplexer> mpEventMultiplexer; + bool mbIsValid; + ToolBarList maToolBarList; + ToolBarShellList maToolBarShellList; + Reference<frame::XLayoutManager> mxLayouter; + sal_Int32 mnLockCount; + bool mbPreUpdatePending; + bool mbPostUpdatePending; + /** The layouter locks manage the locking of the XLayoutManager. The + lock() and unlock() functions are not called directly because the + (final) unlocking is usually done asynchronously *after* the + list of requested toolbars is updated. + */ + ::std::unique_ptr<LayouterLock> mpSynchronousLayouterLock; + ::std::unique_ptr<LayouterLock> mpAsynchronousLayouterLock; + ::std::unique_ptr<ViewShellManager::UpdateLock, o3tl::default_delete<ViewShellManager::UpdateLock>> mpViewShellManagerLock; + ImplSVEvent * mnPendingUpdateCall; + ImplSVEvent * mnPendingSetValidCall; + ToolBarRules maToolBarRules; + + static OUString GetToolBarResourceName (std::u16string_view rsBaseName); + bool CheckPlugInMode (std::u16string_view rsName) const; + + DECL_LINK(UpdateCallback, void *, void); + DECL_LINK(EventMultiplexerCallback, sd::tools::EventMultiplexerEvent&, void); + DECL_LINK(SetValidCallback, void*, void); +}; + +//===== ToolBarManager ======================================================== + +std::shared_ptr<ToolBarManager> ToolBarManager::Create ( + ViewShellBase& rBase, + const std::shared_ptr<sd::tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager) +{ + std::shared_ptr<ToolBarManager> pManager (new ToolBarManager()); + pManager->mpImpl.reset( + new Implementation(rBase,rpMultiplexer,rpViewShellManager,pManager)); + return pManager; +} + +ToolBarManager::ToolBarManager() +{ +} + +ToolBarManager::~ToolBarManager() +{ +} + +void ToolBarManager::Shutdown() +{ + if (mpImpl != nullptr) + mpImpl.reset(); +} + +void ToolBarManager::ResetToolBars (ToolBarGroup eGroup) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + } +} + +void ToolBarManager::ResetAllToolBars() +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetAllToolBars(); + } +} + +void ToolBarManager::AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->AddToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->AddToolBarShell(eGroup,nToolBarId); + } +} + +void ToolBarManager::RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->RemoveToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::SetToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + mpImpl->AddToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::SetToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + mpImpl->AddToolBarShell(eGroup,nToolBarId); + } +} + +void ToolBarManager::PreUpdate() +{ + if (mpImpl != nullptr) + mpImpl->PreUpdate(); +} + +void ToolBarManager::RequestUpdate() +{ + if (mpImpl != nullptr) + mpImpl->RequestUpdate(); +} + +void ToolBarManager::LockViewShellManager() +{ + if (mpImpl != nullptr) + mpImpl->LockViewShellManager(); +} + +void ToolBarManager::LockUpdate() +{ + if (mpImpl != nullptr) + mpImpl->LockUpdate(); +} + +void ToolBarManager::UnlockUpdate() +{ + if (mpImpl != nullptr) + mpImpl->UnlockUpdate(); +} + +void ToolBarManager::MainViewShellChanged () +{ + if (mpImpl != nullptr) + { + mpImpl->ReleaseAllToolBarShells(); + mpImpl->GetToolBarRules().MainViewShellChanged(ViewShell::ST_NONE); + } +} + +void ToolBarManager::MainViewShellChanged (const ViewShell& rMainViewShell) +{ + if (mpImpl != nullptr) + { + mpImpl->ReleaseAllToolBarShells(); + mpImpl->GetToolBarRules().MainViewShellChanged(rMainViewShell); + } +} + +void ToolBarManager::SelectionHasChanged ( + const ViewShell& rViewShell, + const SdrView& rView) +{ + if (mpImpl != nullptr) + mpImpl->GetToolBarRules().SelectionHasChanged(rViewShell,rView); +} + +void ToolBarManager::ToolBarsDestroyed() +{ + if (mpImpl != nullptr) + mpImpl->ToolBarsDestroyed(); +} + +//===== ToolBarManager::Implementation ======================================= + +ToolBarManager::Implementation::Implementation ( + ViewShellBase& rBase, + const std::shared_ptr<sd::tools::EventMultiplexer>& rpMultiplexer, + const std::shared_ptr<ViewShellManager>& rpViewShellManager, + const std::shared_ptr<ToolBarManager>& rpToolBarManager) + : mrBase(rBase), + mpEventMultiplexer(rpMultiplexer), + mbIsValid(false), + mnLockCount(0), + mbPreUpdatePending(false), + mbPostUpdatePending(false), + mnPendingUpdateCall(nullptr), + mnPendingSetValidCall(nullptr), + maToolBarRules(rpToolBarManager,rpViewShellManager) +{ + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this,ToolBarManager::Implementation,EventMultiplexerCallback)); + mpEventMultiplexer->AddEventListener( aLink ); +} + +/** The order of statements is important. + First unregister listeners, which may post user events. + Then remove pending user events. +*/ +ToolBarManager::Implementation::~Implementation() +{ + // Unregister at broadcasters. + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this,ToolBarManager::Implementation,EventMultiplexerCallback)); + mpEventMultiplexer->RemoveEventListener(aLink); + + // Abort pending user calls. + if (mnPendingUpdateCall != nullptr) + Application::RemoveUserEvent(mnPendingUpdateCall); + if (mnPendingSetValidCall != nullptr) + Application::RemoveUserEvent(mnPendingSetValidCall); +} + +void ToolBarManager::Implementation::ToolBarsDestroyed() +{ + maToolBarList.MarkAllToolBarsAsNotActive(); +} + +void ToolBarManager::Implementation::SetValid (bool bValid) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (mbIsValid == bValid) + return; + + UpdateLockImplementation aUpdateLock (*this); + + mbIsValid = bValid; + if (mbIsValid) + { + Reference<frame::XFrame> xFrame; + if (mrBase.GetViewFrame() != nullptr) + xFrame = mrBase.GetViewFrame()->GetFrame().GetFrameInterface(); + try + { + Reference<beans::XPropertySet> xFrameProperties (xFrame, UNO_QUERY_THROW); + Any aValue (xFrameProperties->getPropertyValue("LayoutManager")); + aValue >>= mxLayouter; + // tdf#119997 if mpSynchronousLayouterLock was created before mxLayouter was + // set then update it now that its available + if (mpSynchronousLayouterLock && !mpSynchronousLayouterLock->is()) + mpSynchronousLayouterLock.reset(new LayouterLock(mxLayouter)); + } + catch (const RuntimeException&) + { + } + + GetToolBarRules().Update(mrBase); + } + else + { + ResetAllToolBars(); + mxLayouter = nullptr; + } +} + +void ToolBarManager::Implementation::ResetToolBars (ToolBarGroup eGroup) +{ + ::osl::MutexGuard aGuard(maMutex); + + maToolBarList.ClearGroup(eGroup); + maToolBarShellList.ClearGroup(eGroup); + + mbPreUpdatePending = true; +} + +void ToolBarManager::Implementation::ResetAllToolBars() +{ + SAL_INFO("sd.view", __func__ << ": resetting all tool bars"); + for (auto i : o3tl::enumrange<ToolBarGroup>()) + ResetToolBars(i); +} + +void ToolBarManager::Implementation::AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (CheckPlugInMode(rsToolBarName)) + { + maToolBarList.AddToolBar(eGroup,rsToolBarName); + + mbPostUpdatePending = true; + if (mnLockCount == 0) + PostUpdate(); + } +} + +void ToolBarManager::Implementation::RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (maToolBarList.RemoveToolBar(eGroup,rsToolBarName)) + { + mbPreUpdatePending = true; + if (mnLockCount == 0) + PreUpdate(); + } +} + +void ToolBarManager::Implementation::AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + maToolBarShellList.AddShellId(eGroup,nToolBarId); + GetToolBarRules().SubShellAdded(eGroup, nToolBarId); + } +} + +void ToolBarManager::Implementation::ReleaseAllToolBarShells() +{ + maToolBarShellList.ReleaseAllShells(GetToolBarRules()); + maToolBarShellList.UpdateShells(mrBase.GetMainViewShell(), mrBase.GetViewShellManager()); +} + +void ToolBarManager::Implementation::RequestUpdate() +{ + if (mnPendingUpdateCall == nullptr) + { + mnPendingUpdateCall = Application::PostUserEvent( + LINK(this,ToolBarManager::Implementation,UpdateCallback)); + } +} + +void ToolBarManager::Implementation::PreUpdate() +{ + ::osl::MutexGuard aGuard(maMutex); + + if (!(mbIsValid + && mbPreUpdatePending + && mxLayouter.is())) + return; + + mbPreUpdatePending = false; + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PreUpdate ["); + + // Get the list of tool bars that are not used anymore and are to be + // deactivated. + std::vector<OUString> aToolBars; + maToolBarList.GetToolBarsToDeactivate(aToolBars); + + // Turn off the tool bars. + for (const auto& aToolBar : aToolBars) + { + OUString sFullName (GetToolBarResourceName(aToolBar)); + SAL_INFO("sd.view", __func__ << ": turning off tool bar " << sFullName); + mxLayouter->destroyElement(sFullName); + maToolBarList.MarkToolBarAsNotActive(aToolBar); + } + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PreUpdate ]"); +} + +void ToolBarManager::Implementation::PostUpdate() +{ + ::osl::MutexGuard aGuard(maMutex); + + if (!(mbIsValid + && mbPostUpdatePending + && mxLayouter.is())) + return; + + mbPostUpdatePending = false; + + // Create the list of requested tool bars. + std::vector<OUString> aToolBars; + maToolBarList.GetToolBarsToActivate(aToolBars); + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PostUpdate ["); + + // Turn on the tool bars that are visible in the new context. + for (const auto& aToolBar : aToolBars) + { + OUString sFullName (GetToolBarResourceName(aToolBar)); + SAL_INFO("sd.view", __func__ << ": turning on tool bar " << sFullName); + mxLayouter->requestElement(sFullName); + maToolBarList.MarkToolBarAsActive(aToolBar); + } + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PostUpdate ]"); +} + +void ToolBarManager::Implementation::LockViewShellManager() +{ + if (mpViewShellManagerLock == nullptr) + mpViewShellManagerLock.reset( + new ViewShellManager::UpdateLock(mrBase.GetViewShellManager())); +} + +void ToolBarManager::Implementation::LockUpdate() +{ + SAL_INFO("sd.view", __func__ << ": LockUpdate " << mnLockCount); + ::osl::MutexGuard aGuard(maMutex); + + DBG_ASSERT(mnLockCount<100, "ToolBarManager lock count unusually high"); + if (mnLockCount == 0) + { + OSL_ASSERT(mpSynchronousLayouterLock == nullptr); + + mpSynchronousLayouterLock.reset(new LayouterLock(mxLayouter)); + } + ++mnLockCount; +} + +void ToolBarManager::Implementation::UnlockUpdate() +{ + SAL_INFO("sd.view", __func__ << ": UnlockUpdate " << mnLockCount); + ::osl::MutexGuard aGuard(maMutex); + + OSL_ASSERT(mnLockCount>0); + --mnLockCount; + if (mnLockCount == 0) + { + Update(std::move(mpSynchronousLayouterLock)); + } +} + +void ToolBarManager::Implementation::Update ( + ::std::unique_ptr<LayouterLock> pLocalLayouterLock) +{ + // When the lock is released and there are pending changes to the set of + // tool bars then update this set now. + if (mnLockCount != 0) + return; + + // During creation of ViewShellBase we may have the situation that + // the controller has already been created and attached to the frame + // but that the ToolBarManager has not yet completed its + // initialization (by initializing the mxLayouter member.) We do + // this here so that we do not have to wait for the next Update() + // call to show the tool bars. + if (mnPendingSetValidCall != nullptr) + { + Application::RemoveUserEvent(mnPendingSetValidCall); + mnPendingSetValidCall = nullptr; + SetValid(true); + } + + if (mbIsValid && mxLayouter.is() && (mbPreUpdatePending || mbPostUpdatePending)) + { + // 1) Release UNO tool bars that are no longer used. Do this + // now so that they are not updated when the SFX shell stack is + // modified. + if (mbPreUpdatePending) + PreUpdate(); + + // 2) Update the requested shells that represent tool bar + // functionality. Those that are not used anymore are + // deactivated now. Those that are missing are activated in the + // next step together with the view shells. + if (mpViewShellManagerLock == nullptr) + mpViewShellManagerLock.reset( + new ViewShellManager::UpdateLock(mrBase.GetViewShellManager())); + maToolBarShellList.UpdateShells( + mrBase.GetMainViewShell(), + mrBase.GetViewShellManager()); + + // 3) Unlock the ViewShellManager::UpdateLock. This updates the + // shell stack. + mpViewShellManagerLock.reset(); + + // 4) Make the UNO tool bars visible. The outstanding call to + // PostUpdate() is done via PostUserEvent() so that it is + // guaranteed to be executed when the SFX shell stack has been + // updated (under the assumption that our lock to the + // ViewShellManager was the only one open. If that is not the + // case then all should still be well but not as fast.) + + // Note that the lock count may have been increased since + // entering this method. In that case one of the next + // UnlockUpdate() calls will post the UpdateCallback. + if (mnLockCount==0) + { + mpAsynchronousLayouterLock = std::move(pLocalLayouterLock); + if (mnPendingUpdateCall==nullptr) + { + mnPendingUpdateCall = Application::PostUserEvent( + LINK(this,ToolBarManager::Implementation,UpdateCallback)); + } + } + } + else + { + mpViewShellManagerLock.reset(); + pLocalLayouterLock.reset(); + } +} + +IMPL_LINK_NOARG(ToolBarManager::Implementation, UpdateCallback, void*, void) +{ + mnPendingUpdateCall = nullptr; + if (mnLockCount == 0) + { + if (mbPreUpdatePending) + PreUpdate(); + if (mbPostUpdatePending) + PostUpdate(); + if (mbIsValid && mxLayouter.is()) + mpAsynchronousLayouterLock.reset(); + } +} + +IMPL_LINK(ToolBarManager::Implementation,EventMultiplexerCallback, + sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + SolarMutexGuard g; + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::ControllerAttached: + if (mnPendingSetValidCall == nullptr) + mnPendingSetValidCall + = Application::PostUserEvent(LINK(this,Implementation,SetValidCallback)); + break; + + case EventMultiplexerEventId::ControllerDetached: + SetValid(false); + break; + + default: break; + } +} + +IMPL_LINK_NOARG(ToolBarManager::Implementation, SetValidCallback, void*, void) +{ + mnPendingSetValidCall = nullptr; + SetValid(true); +} + +OUString ToolBarManager::Implementation::GetToolBarResourceName ( + std::u16string_view rsBaseName) +{ + return OUString::Concat("private:resource/toolbar/") + rsBaseName; +} + +bool ToolBarManager::Implementation::CheckPlugInMode (std::u16string_view rsName) const +{ + bool bValid (false); + + // Determine the plug in mode. + bool bIsPlugInMode (false); + do + { + SfxObjectShell* pObjectShell = mrBase.GetObjectShell(); + if (pObjectShell == nullptr) + break; + + SfxMedium* pMedium = pObjectShell->GetMedium(); + if (pMedium == nullptr) + break; + + const SfxBoolItem* pViewOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pMedium->GetItemSet(), SID_VIEWONLY, false); + if (pViewOnlyItem == nullptr) + break; + + bIsPlugInMode = pViewOnlyItem->GetValue(); + } + while (false); + + if (rsName == msViewerToolBar) + bValid = bIsPlugInMode; + else + bValid = ! bIsPlugInMode; + + return bValid; +} + +} // end of namespace sd + +namespace { + +using namespace ::sd; + +//===== LayouterLock ========================================================== + +LayouterLock::LayouterLock (const Reference<frame::XLayoutManager>& rxLayouter) + : mxLayouter(rxLayouter) +{ + SAL_INFO("sd.view", __func__ << ": LayouterLock " << (mxLayouter.is() ? 1 :0)); + if (mxLayouter.is()) + mxLayouter->lock(); +} + +LayouterLock::~LayouterLock() +{ + SAL_INFO("sd.view", __func__ << ": ~LayouterLock " << (mxLayouter.is() ? 1 :0)); + if (mxLayouter.is()) + mxLayouter->unlock(); +} + +//===== ToolBarRules ========================================================== + +ToolBarRules::ToolBarRules ( + const std::shared_ptr<sd::ToolBarManager>& rpToolBarManager, + const std::shared_ptr<sd::ViewShellManager>& rpViewShellManager) + : mpToolBarManager(rpToolBarManager), + mpViewShellManager(rpViewShellManager) +{ +} + +void ToolBarRules::Update (ViewShellBase const & rBase) +{ + ViewShell* pMainViewShell = rBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + MainViewShellChanged(pMainViewShell->GetShellType()); + if (pMainViewShell->GetView()) + SelectionHasChanged (*pMainViewShell, *pMainViewShell->GetView()); + } + else + MainViewShellChanged(ViewShell::ST_NONE); +} + +void ToolBarRules::MainViewShellChanged (ViewShell::ShellType nShellType) +{ + ::sd::ToolBarManager::UpdateLock aToolBarManagerLock (mpToolBarManager); + ::sd::ViewShellManager::UpdateLock aViewShellManagerLock (mpViewShellManager); + + mpToolBarManager->ResetAllToolBars(); + + switch(nShellType) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_NOTES: + case ::sd::ViewShell::ST_HANDOUT: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOptionsToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + break; + + case ::sd::ViewShell::ST_DRAW: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOptionsToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + break; + + case ViewShell::ST_OUTLINE: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOutlineToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + mpToolBarManager->AddToolBarShell( + ToolBarManager::ToolBarGroup::Permanent, ToolbarId::Draw_Text_Toolbox_Sd); + break; + + case ViewShell::ST_SLIDE_SORTER: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msSlideSorterToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msSlideSorterObjectBar); + break; + + case ViewShell::ST_NONE: + case ViewShell::ST_PRESENTATION: + case ViewShell::ST_SIDEBAR: + default: + break; + } +} + +void ToolBarRules::MainViewShellChanged (const ViewShell& rMainViewShell) +{ + ::sd::ToolBarManager::UpdateLock aToolBarManagerLock (mpToolBarManager); + ::sd::ViewShellManager::UpdateLock aViewShellManagerLock (mpViewShellManager); + + MainViewShellChanged(rMainViewShell.GetShellType()); + switch(rMainViewShell.GetShellType()) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_DRAW: + case ::sd::ViewShell::ST_NOTES: + { + const DrawViewShell* pDrawViewShell + = dynamic_cast<const DrawViewShell*>(&rMainViewShell); + if (pDrawViewShell != nullptr) + { + if (pDrawViewShell->GetEditMode() == EditMode::MasterPage) + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::MasterMode, + ToolBarManager::msMasterViewToolBar); + else if ( rMainViewShell.GetShellType() != ::sd::ViewShell::ST_DRAW ) + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::CommonTask, + ToolBarManager::msCommonTaskToolBar); + } + break; + } + + default: + break; + } +} + +void ToolBarRules::SelectionHasChanged ( + const ::sd::ViewShell& rViewShell, + const SdrView& rView) +{ + ::sd::ToolBarManager::UpdateLock aLock (mpToolBarManager); + mpToolBarManager->LockViewShellManager(); + bool bTextEdit = rView.IsTextEdit(); + + mpToolBarManager->ResetToolBars(ToolBarManager::ToolBarGroup::Function); + + switch (rView.GetContext()) + { + case SdrViewContext::Graphic: + if( !bTextEdit ) + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Graf_Toolbox); + break; + + case SdrViewContext::Media: + if( !bTextEdit ) + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Media_Toolbox); + break; + + case SdrViewContext::Table: + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Table_Toolbox); + bTextEdit = true; + break; + + case SdrViewContext::Standard: + default: + if( !bTextEdit ) + { + switch(rViewShell.GetShellType()) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_DRAW: + case ::sd::ViewShell::ST_NOTES: + case ::sd::ViewShell::ST_HANDOUT: + mpToolBarManager->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + break; + default: + break; + } + break; + } + } + + if( bTextEdit ) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Text_Toolbox_Sd); + + SdrView* pView = &const_cast<SdrView&>(rView); + // Check if the extrusion tool bar and the fontwork tool bar have to + // be activated. + if (svx::checkForSelectedCustomShapes(pView, true /* bOnlyExtruded */ )) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Svx_Extrusion_Bar); + + if (svx::checkForSelectedFontWork(pView)) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Svx_Fontwork_Bar); + + // Switch on additional context-sensitive tool bars. + if (rView.GetContext() == SdrViewContext::PointEdit) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Bezier_Toolbox_Sd); +} + +void ToolBarRules::SubShellAdded ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId) +{ + // For some tool bar shells (those defined in sd) we have to add the + // actual tool bar here. + switch (nShellId) + { + case ToolbarId::Draw_Graf_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msGraphicObjectBar); + break; + + case ToolbarId::Draw_Media_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msMediaObjectBar); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msTextObjectBar); + break; + + case ToolbarId::Bezier_Toolbox_Sd: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msBezierObjectBar); + break; + + case ToolbarId::Draw_Table_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msTableObjectBar); + break; + + default: + break; + } +} + +void ToolBarRules::SubShellRemoved ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId) +{ + // For some tool bar shells (those defined in sd) we have to add the + // actual tool bar here. + switch (nShellId) + { + case ToolbarId::Draw_Graf_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msGraphicObjectBar); + break; + + case ToolbarId::Draw_Media_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msMediaObjectBar); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msTextObjectBar); + break; + + case ToolbarId::Bezier_Toolbox_Sd: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msBezierObjectBar); + break; + + case ToolbarId::Draw_Table_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msTableObjectBar); + break; + + default: + break; + } +} + +//===== ToolBarList =========================================================== + +ToolBarList::ToolBarList() +{ +} + +void ToolBarList::ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + { + iGroup->second.clear(); + } +} + +void ToolBarList::AddToolBar ( + sd::ToolBarManager::ToolBarGroup eGroup, + const OUString& rsName) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup == maGroups.end()) + iGroup = maGroups.emplace(eGroup,std::vector<OUString>()).first; + + if (iGroup != maGroups.end()) + { + auto iBar (std::find(iGroup->second.cbegin(),iGroup->second.cend(),rsName)); + if (iBar == iGroup->second.cend()) + { + iGroup->second.push_back(rsName); + } + } +} + +bool ToolBarList::RemoveToolBar ( + sd::ToolBarManager::ToolBarGroup eGroup, + const OUString& rsName) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + { + auto iBar (std::find(iGroup->second.begin(),iGroup->second.end(),rsName)); + if (iBar != iGroup->second.end()) + { + iGroup->second.erase(iBar); + return true; + } + } + return false; +} + +void ToolBarList::MakeRequestedToolBarList (std::vector<OUString>& rRequestedToolBars) const +{ + for (auto eGroup : o3tl::enumrange<sd::ToolBarManager::ToolBarGroup>()) + { + Groups::const_iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + rRequestedToolBars.insert( rRequestedToolBars.end(), + iGroup->second.begin(), + iGroup->second.end() ); + } +} + +void ToolBarList::GetToolBarsToActivate (std::vector<OUString>& rToolBars) const +{ + std::vector<OUString> aRequestedToolBars; + MakeRequestedToolBarList(aRequestedToolBars); + + for (const auto& aToolBar : aRequestedToolBars) + { + if (::std::find(maActiveToolBars.begin(),maActiveToolBars.end(),aToolBar) + == maActiveToolBars.end()) + { + rToolBars.push_back(aToolBar); + } + } +} + +void ToolBarList::GetToolBarsToDeactivate (std::vector<OUString>& rToolBars) const +{ + std::vector<OUString> aRequestedToolBars; + MakeRequestedToolBarList(aRequestedToolBars); + + for (auto& aToolBar : maActiveToolBars) + { + if (::std::find(aRequestedToolBars.begin(),aRequestedToolBars.end(),aToolBar) + == aRequestedToolBars.end()) + { + rToolBars.push_back(aToolBar); + } + } +} + +void ToolBarList::MarkToolBarAsActive (const OUString& rsName) +{ + maActiveToolBars.push_back(rsName); +} + +void ToolBarList::MarkToolBarAsNotActive (const OUString& rsName) +{ + maActiveToolBars.erase( + ::std::find(maActiveToolBars.begin(),maActiveToolBars.end(), rsName)); +} + +void ToolBarList::MarkAllToolBarsAsNotActive() +{ + maActiveToolBars.clear(); +} + +//===== ToolBarShellList ====================================================== + +ToolBarShellList::ShellDescriptor::ShellDescriptor ( + ShellId nId, + sd::ToolBarManager::ToolBarGroup eGroup) + : mnId(nId), + meGroup(eGroup) +{ +} + +ToolBarShellList::ToolBarShellList() +{ +} + +void ToolBarShellList::ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup) +{ + for (GroupedShellList::iterator iDescriptor = maNewList.begin(); iDescriptor != maNewList.end(); ) + if (iDescriptor->meGroup == eGroup) + iDescriptor = maNewList.erase(iDescriptor); + else + ++iDescriptor; +} + +void ToolBarShellList::AddShellId (sd::ToolBarManager::ToolBarGroup eGroup, sd::ShellId nId) +{ + // Make sure that the shell is not added twice (and possibly in + // different groups.) + ShellDescriptor aDescriptor (nId,eGroup); + GroupedShellList::iterator iDescriptor (maNewList.find(aDescriptor)); + if (iDescriptor != maNewList.end()) + { + // The shell is already requested. + if (iDescriptor->meGroup != eGroup) + { + // It is now being requested for another group. + // (Is this an error?) + // Move it to that group. + maNewList.erase(iDescriptor); + maNewList.insert(aDescriptor); + } + // else nothing to do. + } + else + maNewList.insert(aDescriptor); +} + +void ToolBarShellList::ReleaseAllShells (ToolBarRules& rRules) +{ + // Release the currently active tool bars. + GroupedShellList aList (maCurrentList); + for (const auto& rDescriptor : aList) + { + rRules.SubShellRemoved(rDescriptor.meGroup, rDescriptor.mnId); + } + + // Clear the list of requested tool bars. + maNewList.clear(); +} + +void ToolBarShellList::UpdateShells ( + const std::shared_ptr<ViewShell>& rpMainViewShell, + const std::shared_ptr<ViewShellManager>& rpManager) +{ + if (rpMainViewShell == nullptr) + return; + + GroupedShellList aList; + + // Deactivate shells that are in maCurrentList, but not in + // maNewList. + ::std::set_difference(maCurrentList.begin(), maCurrentList.end(), + maNewList.begin(), maNewList.end(), + std::insert_iterator<GroupedShellList>(aList,aList.begin())); + for (const auto& rShell : aList) + { + SAL_INFO("sd.view", __func__ << ": deactivating tool bar shell " << static_cast<sal_uInt32>(rShell.mnId)); + rpManager->DeactivateSubShell(*rpMainViewShell, rShell.mnId); + } + + // Activate shells that are in maNewList, but not in + // maCurrentList. + aList.clear(); + ::std::set_difference(maNewList.begin(), maNewList.end(), + maCurrentList.begin(), maCurrentList.end(), + std::insert_iterator<GroupedShellList>(aList,aList.begin())); + for (const auto& rShell : aList) + { + SAL_INFO("sd.view", __func__ << ": activating tool bar shell " << static_cast<sal_uInt32>(rShell.mnId)); + rpManager->ActivateSubShell(*rpMainViewShell, rShell.mnId); + } + + // The maNewList now reflects the current state and thus is made + // maCurrentList. + maCurrentList = maNewList; +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |