diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sd/source/ui/sidebar | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sd/source/ui/sidebar')
36 files changed, 8878 insertions, 0 deletions
diff --git a/sd/source/ui/sidebar/AllMasterPagesSelector.cxx b/sd/source/ui/sidebar/AllMasterPagesSelector.cxx new file mode 100644 index 000000000..76e056120 --- /dev/null +++ b/sd/source/ui/sidebar/AllMasterPagesSelector.cxx @@ -0,0 +1,180 @@ +/* -*- 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 "AllMasterPagesSelector.hxx" +#include <ViewShellBase.hxx> +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" +#include <helpids.h> + +#include <set> + +namespace { + +using namespace sd::sidebar; + +int GetURLPriority (const SharedMasterPageDescriptor& rpDescriptor) +{ + int nPriority (0); + switch (rpDescriptor->GetURLClassification()) + { + case MasterPageDescriptor::URLCLASS_USER: nPriority = 0; break; + case MasterPageDescriptor::URLCLASS_LAYOUT: nPriority = 1; break; + case MasterPageDescriptor::URLCLASS_PRESENTATION: nPriority = 2; break; + case MasterPageDescriptor::URLCLASS_OTHER: nPriority = 3; break; + case MasterPageDescriptor::URLCLASS_UNKNOWN: nPriority = 4; break; + default: + case MasterPageDescriptor::URLCLASS_UNDETERMINED: nPriority = 5; break; + } + return nPriority; +} + +class MasterPageDescriptorOrder +{ +public: + bool operator() ( + const SharedMasterPageDescriptor& rp1, + const SharedMasterPageDescriptor& rp2) const + { + if (rp1->meOrigin == MasterPageContainer::DEFAULT) + return true; + else if (rp2->meOrigin == MasterPageContainer::DEFAULT) + return false; + else if (rp1->GetURLClassification() == rp2->GetURLClassification()) + return rp1->mnTemplateIndex < rp2->mnTemplateIndex; + else + return GetURLPriority(rp1) < GetURLPriority(rp2); + } +}; + +} // end of anonymous namespace + +namespace sd::sidebar { + +class AllMasterPagesSelector::SortedMasterPageDescriptorList + : public ::std::set<SharedMasterPageDescriptor,MasterPageDescriptorOrder> +{ +public: + SortedMasterPageDescriptorList() {} +}; + +std::unique_ptr<PanelLayout> AllMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared<MasterPageContainer>(); + + auto xSelector(std::make_unique<AllMasterPagesSelector>( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId(HID_SD_TASK_PANE_PREVIEW_ALL); + + return xSelector; +} + +AllMasterPagesSelector::AllMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : MasterPagesSelector(pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanelall.ui", "allvalueset"), + mpSortedMasterPages(new SortedMasterPageDescriptorList()) +{ + MasterPagesSelector::Fill(); +} + +AllMasterPagesSelector::~AllMasterPagesSelector() +{ +} + +void AllMasterPagesSelector::Fill (ItemList& rItemList) +{ + if (mpSortedMasterPages->empty()) + UpdateMasterPageList(); + UpdatePageSet(rItemList); +} + +void AllMasterPagesSelector::NotifyContainerChangeEvent ( + const MasterPageContainerChangeEvent& rEvent) +{ + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::CHILD_ADDED: + AddItem(rEvent.maChildToken); + MasterPagesSelector::Fill(); + break; + + case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED: + mpSortedMasterPages->clear(); + MasterPagesSelector::Fill(); + break; + + default: + MasterPagesSelector::NotifyContainerChangeEvent(rEvent); + break; + } +} + +void AllMasterPagesSelector::UpdateMasterPageList() +{ + mpSortedMasterPages->clear(); + int nTokenCount = mpContainer->GetTokenCount(); + for (int i=0; i<nTokenCount; i++) + AddItem(mpContainer->GetTokenForIndex(i)); +} + +void AllMasterPagesSelector::AddItem (MasterPageContainer::Token aToken) +{ + switch (mpContainer->GetOriginForToken(aToken)) + { + case MasterPageContainer::DEFAULT: + case MasterPageContainer::TEMPLATE: + // Templates are added only when coming from the + // MasterPageContainerFiller so that they have an id which + // defines their place in the list. Templates (pre) loaded from + // RecentlyUsedMasterPages are ignored (they will be loaded + // later by the MasterPageContainerFiller.) + if (mpContainer->GetTemplateIndexForToken(aToken) >= 0) + mpSortedMasterPages->insert(mpContainer->GetDescriptorForToken(aToken)); + break; + + default: + break; + } +} + +void AllMasterPagesSelector::UpdatePageSet (ItemList& rItemList) +{ + for (const auto& rxDescriptor : *mpSortedMasterPages) + rItemList.push_back(rxDescriptor->maToken); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/AllMasterPagesSelector.hxx b/sd/source/ui/sidebar/AllMasterPagesSelector.hxx new file mode 100644 index 000000000..982a2ec52 --- /dev/null +++ b/sd/source/ui/sidebar/AllMasterPagesSelector.hxx @@ -0,0 +1,80 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPagesSelector.hxx" + +#include <memory> + +namespace sd::sidebar { + +/** Show a list of all available master pages so that the user can assign + them to the document. +*/ +class AllMasterPagesSelector + : public MasterPagesSelector +{ + friend class VclPtrInstance<AllMasterPagesSelector>; +public: + static std::unique_ptr<PanelLayout> Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + + AllMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~AllMasterPagesSelector() override; + + + /** Scan the set of templates for the ones whose first master pages are + shown by this control and store them in the MasterPageContainer. + */ + virtual void Fill (ItemList& rItemList) override; + +protected: + virtual void NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent) override; + +private: + /** The list of master pages displayed by this class. + */ + class SortedMasterPageDescriptorList; + ::std::unique_ptr<SortedMasterPageDescriptorList> mpSortedMasterPages; + + void AddItem (MasterPageContainer::Token aToken); + + /** Add all items in the internal master page list into the given list. + */ + void UpdatePageSet (ItemList& rItemList); + + /** Update the internal list of master pages that are to show in the + control. + */ + void UpdateMasterPageList(); + + using MasterPagesSelector::Fill; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx b/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx new file mode 100644 index 000000000..269099edd --- /dev/null +++ b/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx @@ -0,0 +1,263 @@ +/* -*- 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 "CurrentMasterPagesSelector.hxx" +#include "PreviewValueSet.hxx" +#include <ViewShellBase.hxx> +#include <DrawViewShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include "MasterPageContainer.hxx" +#include "MasterPageContainerProviders.hxx" +#include "MasterPageDescriptor.hxx" +#include <EventMultiplexer.hxx> +#include <DrawDocShell.hxx> +#include <osl/diagnose.h> + +#include <helpids.h> + +#include <set> + +using namespace ::com::sun::star; + +namespace sd::sidebar { + +std::unique_ptr<PanelLayout> CurrentMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared<MasterPageContainer>(); + + auto xSelector(std::make_unique<CurrentMasterPagesSelector>( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId( HID_SD_TASK_PANE_PREVIEW_CURRENT ); + + return xSelector; +} + +CurrentMasterPagesSelector::CurrentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : MasterPagesSelector (pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanel.ui", "usedvalueset") +{ + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this,CurrentMasterPagesSelector,EventMultiplexerListener)); + rBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +CurrentMasterPagesSelector::~CurrentMasterPagesSelector() +{ + if (mrDocument.GetDocSh() != nullptr) + { + EndListening(*mrDocument.GetDocSh()); + } + else + { + OSL_ASSERT(mrDocument.GetDocSh() != nullptr); + } + + Link<sd::tools::EventMultiplexerEvent&,void> aLink (LINK(this,CurrentMasterPagesSelector,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->RemoveEventListener(aLink); +} + +void CurrentMasterPagesSelector::LateInit() +{ + MasterPagesSelector::LateInit(); + MasterPagesSelector::Fill(); + if (mrDocument.GetDocSh() != nullptr) + { + StartListening(*mrDocument.GetDocSh()); + } + else + { + OSL_ASSERT(mrDocument.GetDocSh() != nullptr); + } +} + +void CurrentMasterPagesSelector::Fill (ItemList& rItemList) +{ + sal_uInt16 nPageCount = mrDocument.GetMasterSdPageCount(PageKind::Standard); + // Remember the names of the master pages that have been inserted to + // avoid double insertion. + ::std::set<OUString> aMasterPageNames; + for (sal_uInt16 nIndex=0; nIndex<nPageCount; nIndex++) + { + SdPage* pMasterPage = mrDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage == nullptr) + continue; + + // Use the name of the master page to avoid duplicate entries. + OUString sName (pMasterPage->GetName()); + if (!aMasterPageNames.insert(sName).second) + continue; + + // Look up the master page in the container and, when it is not yet + // in it, insert it. + MasterPageContainer::Token aToken = mpContainer->GetTokenForPageObject(pMasterPage); + if (aToken == MasterPageContainer::NIL_TOKEN) + { + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::MASTERPAGE, + nIndex, + OUString(), + pMasterPage->GetName(), + OUString(), + pMasterPage->IsPrecious(), + std::make_shared<ExistingPageProvider>(pMasterPage), + std::make_shared<PagePreviewProvider>()); + aToken = mpContainer->PutMasterPage(pDescriptor); + } + + rItemList.push_back(aToken); + } +} + +OUString CurrentMasterPagesSelector::GetContextMenuUIFile() const +{ + return "modules/simpress/ui/currentmastermenu.ui"; +} + +void CurrentMasterPagesSelector::UpdateSelection() +{ + // Iterate over all pages and for the selected ones put the name of + // their master page into a set. + sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard); + ::std::set<OUString> aNames; + sal_uInt16 nIndex; + bool bLoop (true); + for (nIndex=0; nIndex<nPageCount && bLoop; nIndex++) + { + SdPage* pPage = mrDocument.GetSdPage (nIndex, PageKind::Standard); + if (pPage != nullptr && pPage->IsSelected()) + { + if ( ! pPage->TRG_HasMasterPage()) + { + // One of the pages has no master page. This is an + // indicator for that this method is called in the middle of + // a document change and that the model is not in a valid + // state. Therefore we stop update the selection and wait + // for another call to UpdateSelection when the model is + // valid again. + bLoop = false; + } + else + { + SdrPage& rMasterPage (pPage->TRG_GetMasterPage()); + assert(dynamic_cast<SdPage*>(&rMasterPage)); + aNames.insert(static_cast<SdPage&>(rMasterPage).GetName()); + } + } + } + + // Find the items for the master pages in the set. + sal_uInt16 nItemCount (mxPreviewValueSet->GetItemCount()); + for (nIndex=1; nIndex<=nItemCount && bLoop; nIndex++) + { + OUString sName (mxPreviewValueSet->GetItemText (nIndex)); + if (aNames.find(sName) != aNames.end()) + { + mxPreviewValueSet->SelectItem (nIndex); + } + } +} + +void CurrentMasterPagesSelector::ExecuteCommand(const OString &rIdent) +{ + if (rIdent == "delete") + { + // Check once again that the master page can safely be deleted, + // i.e. is not used. + SdPage* pMasterPage = GetSelectedMasterPage(); + if (pMasterPage != nullptr + && mrDocument.GetMasterPageUserCount(pMasterPage) == 0) + { + // Removing the precious flag so that the following call to + // RemoveUnnecessaryMasterPages() will remove this master page. + pMasterPage->SetPrecious(false); + mrDocument.RemoveUnnecessaryMasterPages(pMasterPage); + } + } + else + MasterPagesSelector::ExecuteCommand(rIdent); +} + +void CurrentMasterPagesSelector::ProcessPopupMenu(weld::Menu& rMenu) +{ + // Disable the delete entry when there is only one master page. + if (mrDocument.GetMasterPageUserCount(GetSelectedMasterPage()) > 0) + rMenu.set_sensitive("delete", false); + + std::shared_ptr<DrawViewShell> pDrawViewShell ( + std::dynamic_pointer_cast<DrawViewShell>(mrBase.GetMainViewShell())); + if (pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + rMenu.set_sensitive("edit", false); + } + + MasterPagesSelector::ProcessPopupMenu(rMenu); +} + +IMPL_LINK(CurrentMasterPagesSelector,EventMultiplexerListener, + sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditModeNormal: + case EventMultiplexerEventId::EditModeMaster: + case EventMultiplexerEventId::SlideSortedSelection: + UpdateSelection(); + break; + + case EventMultiplexerEventId::PageOrder: + // This is tricky. If a master page is removed, moved, or + // added we have to wait until both the notes master page + // and the standard master page have been removed, moved, + // or added. We do this by looking at the number of master + // pages which has to be odd in the consistent state (the + // handout master page is always present). If the number is + // even we ignore the hint. + if (mrBase.GetDocument()->GetMasterPageCount()%2 == 1) + MasterPagesSelector::Fill(); + break; + + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + InvalidatePreview(static_cast<const SdPage*>(rEvent.mpUserData)); + break; + default: break; + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx b/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx new file mode 100644 index 000000000..cd7c27734 --- /dev/null +++ b/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx @@ -0,0 +1,77 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svl/lstner.hxx> + +#include "MasterPagesSelector.hxx" + +namespace sd::tools { class EventMultiplexerEvent; } + +namespace sd::sidebar { + +/** Show the master pages currently used by a SdDrawDocument. +*/ +class CurrentMasterPagesSelector + : public MasterPagesSelector, + public SfxListener +{ + friend class VclPtrInstance<CurrentMasterPagesSelector>; +public: + static std::unique_ptr<PanelLayout> Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + + CurrentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~CurrentMasterPagesSelector() override; + + /** Set the selection so that the master page is selected that is + used by the currently selected page of the document in the + center pane. + */ + void UpdateSelection(); + + /** Copy all master pages that are to be shown into the given list. + */ + virtual void Fill (ItemList& rItemList) override; + + using MasterPagesSelector::Fill; + +protected: + virtual OUString GetContextMenuUIFile() const override; + + virtual void ProcessPopupMenu(weld::Menu& rMenu) override; + virtual void ExecuteCommand(const OString &rIdent) override; + +private: + virtual void LateInit() override; + + DECL_LINK(EventMultiplexerListener,sd::tools::EventMultiplexerEvent&, void); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/DocumentHelper.cxx b/sd/source/ui/sidebar/DocumentHelper.cxx new file mode 100644 index 000000000..00c028868 --- /dev/null +++ b/sd/source/ui/sidebar/DocumentHelper.cxx @@ -0,0 +1,536 @@ +/* -*- 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 "DocumentHelper.hxx" + +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdpage.hxx> +#include <glob.hxx> +#include <unmovss.hxx> +#include <strings.hrc> +#include <sdresid.hxx> +#include <undoback.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XDrawPages.hpp> +#include <stlpool.hxx> +#include <svx/xfillit0.hxx> +#include <svx/svdundo.hxx> +#include <tools/diagnose_ex.h> +#include <xmloff/autolayout.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; + +namespace sd::sidebar { + +SdPage* DocumentHelper::CopyMasterPageToLocalDocument ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage) +{ + SdPage* pNewMasterPage = nullptr; + + do + { + if (pMasterPage == nullptr) + break; + + // Check the presence of the source document. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + + // When the given master page already belongs to the target document + // then there is nothing more to do. + if (&rSourceDocument == &rTargetDocument) + { + pNewMasterPage = pMasterPage; + break; + } + + // Test if the master pages of both the slide and its notes page are + // present. This is not the case when we are called during the + // creation of the slide master page because then the notes master + // page is not there. + sal_uInt16 nSourceMasterPageCount = rSourceDocument.GetMasterPageCount(); + if (nSourceMasterPageCount%2 == 0) + // There should be 1 handout page + n slide masters + n notes + // masters = 2*n+1. An even value indicates that a new slide + // master but not yet the notes master has been inserted. + break; + sal_uInt16 nIndex = pMasterPage->GetPageNum(); + if (nSourceMasterPageCount <= nIndex+1) + break; + // Get the slide master page. + if (pMasterPage != static_cast<SdPage*>( + rSourceDocument.GetMasterPage(nIndex))) + break; + // Get the notes master page. + SdPage* pNotesMasterPage = static_cast<SdPage*>( + rSourceDocument.GetMasterPage(nIndex+1)); + if (pNotesMasterPage == nullptr) + break; + + // Check if a master page with the same name as that of the given + // master page already exists. + bool bPageExists (false); + sal_uInt16 nMasterPageCount(rTargetDocument.GetMasterSdPageCount(PageKind::Standard)); + for (sal_uInt16 nMaster=0; nMaster<nMasterPageCount; nMaster++) + { + SdPage* pCandidate = rTargetDocument.GetMasterSdPage (nMaster, PageKind::Standard); + if (pCandidate->GetName() == pMasterPage->GetName()) + { + bPageExists = true; + pNewMasterPage = pCandidate; + break; + } + } + if (bPageExists) + break; + + // Create a new slide (and its notes page.) + uno::Reference<drawing::XDrawPagesSupplier> xSlideSupplier ( + rTargetDocument.getUnoModel(), uno::UNO_QUERY); + if ( ! xSlideSupplier.is()) + break; + uno::Reference<drawing::XDrawPages> xSlides = + xSlideSupplier->getDrawPages(); + if ( ! xSlides.is()) + break; + xSlides->insertNewByIndex (xSlides->getCount()); + + // Set a layout. + SdPage* pSlide = rTargetDocument.GetSdPage( + rTargetDocument.GetSdPageCount(PageKind::Standard)-1, + PageKind::Standard); + if (pSlide == nullptr) + break; + pSlide->SetAutoLayout(AUTOLAYOUT_TITLE, true); + + // Create a copy of the master page and the associated notes + // master page and insert them into our document. + pNewMasterPage = AddMasterPage(rTargetDocument, pMasterPage); + if (pNewMasterPage==nullptr) + break; + SdPage* pNewNotesMasterPage + = AddMasterPage(rTargetDocument, pNotesMasterPage); + if (pNewNotesMasterPage==nullptr) + break; + + // Make the connection from the new slide to the master page + // (and do the same for the notes page.) + rTargetDocument.SetMasterPage ( + rTargetDocument.GetSdPageCount(PageKind::Standard)-1, + pNewMasterPage->GetName(), + &rTargetDocument, + false, // Connect the new master page with the new slide but + // do not modify other (master) pages. + true); + } + while (false); + + // We are not interested in any automatisms for our modified internal + // document. + rTargetDocument.SetChanged(false); + + return pNewMasterPage; +} + +SdPage* DocumentHelper::GetSlideForMasterPage (SdPage const * pMasterPage) +{ + SdPage* pCandidate = nullptr; + + SdDrawDocument* pDocument = nullptr; + if (pMasterPage != nullptr) + pDocument = dynamic_cast< SdDrawDocument* >(&pMasterPage->getSdrModelFromSdrPage()); + + // Iterate over all pages and check if it references the given master + // page. + if (pDocument!=nullptr && pDocument->GetSdPageCount(PageKind::Standard) > 0) + { + // In most cases a new slide has just been inserted so start with + // the last page. + sal_uInt16 nPageIndex (pDocument->GetSdPageCount(PageKind::Standard)-1); + bool bFound (false); + while ( ! bFound) + { + pCandidate = pDocument->GetSdPage( + nPageIndex, + PageKind::Standard); + if (pCandidate != nullptr) + { + if (static_cast<SdPage*>(&pCandidate->TRG_GetMasterPage()) + == pMasterPage) + { + bFound = true; + break; + } + } + + if (nPageIndex == 0) + break; + else + nPageIndex --; + } + + // If no page was found, that referenced the given master page, reset + // the pointer that is returned. + if ( ! bFound) + pCandidate = nullptr; + } + + return pCandidate; +} + +SdPage* DocumentHelper::AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage) +{ + rtl::Reference<SdPage> pClonedMasterPage; + + if (pMasterPage!=nullptr) + { + try + { + // Duplicate the master page. + pClonedMasterPage = static_cast<SdPage*>(pMasterPage->CloneSdrPage(rTargetDocument).get()); + + // Copy the necessary styles. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + ProvideStyles(rSourceDocument, rTargetDocument, pClonedMasterPage.get()); + + // Copy the precious flag. + pClonedMasterPage->SetPrecious(pMasterPage->IsPrecious()); + + // Now that the styles are available we can insert the cloned + // master page. + rTargetDocument.InsertMasterPage (pClonedMasterPage.get()); + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + pClonedMasterPage = nullptr; + } + catch(const ::std::exception& e) + { + pClonedMasterPage = nullptr; + SAL_WARN("sd", "caught general exception " << e.what()); + } + catch(...) + { + pClonedMasterPage = nullptr; + SAL_WARN("sd", "caught general exception"); + } + } + + return pClonedMasterPage.get(); +} + +void DocumentHelper::ProvideStyles ( + SdDrawDocument const & rSourceDocument, + SdDrawDocument& rTargetDocument, + SdPage const * pPage) +{ + // Get the layout name of the given page. + OUString sLayoutName (pPage->GetLayoutName()); + sal_Int32 nIndex = sLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + sLayoutName = sLayoutName.copy(0, nIndex); + + // Copy the style sheet from source to target document. + SdStyleSheetPool* pSourceStyleSheetPool = + static_cast<SdStyleSheetPool*>(rSourceDocument.GetStyleSheetPool()); + SdStyleSheetPool* pTargetStyleSheetPool = + static_cast<SdStyleSheetPool*>(rTargetDocument.GetStyleSheetPool()); + StyleSheetCopyResultVector aCreatedStyles; + pTargetStyleSheetPool->CopyLayoutSheets ( + sLayoutName, + *pSourceStyleSheetPool, + aCreatedStyles); + + // Add an undo action for the copied style sheets. + if( !aCreatedStyles.empty() ) + { + SfxUndoManager* pUndoManager = rTargetDocument.GetDocSh()->GetUndoManager(); + if (pUndoManager != nullptr) + { + pUndoManager->AddUndoAction ( + std::make_unique<SdMoveStyleSheetsUndoAction>( + &rTargetDocument, + aCreatedStyles, + true)); + } + } +} + +void DocumentHelper::AssignMasterPageToPageList ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) +{ + if (pMasterPage == nullptr || !pMasterPage->IsMasterPage()) + return; + + // Make the layout name by stripping out the layout postfix from the + // layout name of the given master page. + OUString sFullLayoutName(pMasterPage->GetLayoutName()); + OUString sBaseLayoutName (sFullLayoutName); + sal_Int32 nIndex = sBaseLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + sBaseLayoutName = sBaseLayoutName.copy(0, nIndex); + + if (rpPageList->empty()) + return; + + // Create a second list that contains only the valid pointers to + // pages for which an assignment is necessary. + ::std::vector<SdPage*> aCleanedList; + for (const auto& rpPage : *rpPageList) + { + OSL_ASSERT(rpPage!=nullptr && &rpPage->getSdrModelFromSdrPage() == &rTargetDocument); + if (rpPage != nullptr && rpPage->GetLayoutName() != sFullLayoutName) + { + aCleanedList.push_back(rpPage); + } + } + if (aCleanedList.empty() ) + return; + + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = rTargetDocument.GetDocSh()->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + SfxUndoManager* pUndoMgr = rTargetDocument.GetDocSh()->GetUndoManager(); + if( pUndoMgr ) + pUndoMgr->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT), OUString(), 0, nViewShellId); + + SdPage* pMasterPageInDocument = ProvideMasterPage(rTargetDocument,pMasterPage,rpPageList); + if (pMasterPageInDocument == nullptr) + return; + + // Assign the master pages to the given list of pages. + for (const auto& rpPage : aCleanedList) + { + AssignMasterPageToPage ( + pMasterPageInDocument, + sBaseLayoutName, + rpPage); + } + + if( pUndoMgr ) + pUndoMgr->LeaveListAction(); +} + +SdPage* DocumentHelper::AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage, + sal_uInt16 nInsertionIndex) +{ + rtl::Reference<SdPage> pClonedMasterPage; + + if (pMasterPage!=nullptr) + { + // Duplicate the master page. + pClonedMasterPage = static_cast<SdPage*>(pMasterPage->CloneSdrPage(rTargetDocument).get()); + + // Copy the precious flag. + pClonedMasterPage->SetPrecious(pMasterPage->IsPrecious()); + + // Copy the necessary styles. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + ProvideStyles(rSourceDocument, rTargetDocument, pClonedMasterPage.get()); + + // Now that the styles are available we can insert the cloned + // master page. + rTargetDocument.InsertMasterPage (pClonedMasterPage.get(), nInsertionIndex); + + // Adapt the size of the new master page to that of the pages in + // the document. + Size aNewSize (rTargetDocument.GetSdPage(0, pMasterPage->GetPageKind())->GetSize()); + ::tools::Rectangle aBorders ( + pClonedMasterPage->GetLeftBorder(), + pClonedMasterPage->GetUpperBorder(), + pClonedMasterPage->GetRightBorder(), + pClonedMasterPage->GetLowerBorder()); + pClonedMasterPage->ScaleObjects(aNewSize, aBorders, true); + pClonedMasterPage->SetSize(aNewSize); + pClonedMasterPage->CreateTitleAndLayout(true); + } + + return pClonedMasterPage.get(); +} + +/** In here we have to handle three cases: + 1. pPage is a normal slide. We can use SetMasterPage to assign the + master pages to it. + 2. pPage is a master page that is used by at least one slide. We can + assign the master page to these slides. + 3. pPage is a master page that is currently not used by any slide. + We can delete that page and add copies of the given master pages + instead. + + For points 2 and 3 where one master page A is assigned to another B we have + to keep in mind that the master page that page A has already been + inserted into the target document. +*/ +void DocumentHelper::AssignMasterPageToPage ( + SdPage const * pMasterPage, + std::u16string_view rsBaseLayoutName, + SdPage* pPage) +{ + // Leave early when the parameters are invalid. + if (pPage == nullptr || pMasterPage == nullptr) + return; + + SdDrawDocument& rDocument(dynamic_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + + if ( ! pPage->IsMasterPage()) + { + // 1. Remove the background object (so that, if it exists, does + // not override the new master page) and assign the master page to + // the regular slide. + rDocument.GetDocSh()->GetUndoManager()->AddUndoAction( + std::make_unique<SdBackgroundObjUndoAction>( + rDocument, *pPage, pPage->getSdrPageProperties().GetItemSet()), + true); + pPage->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + + rDocument.SetMasterPage ( + (pPage->GetPageNum()-1)/2, + rsBaseLayoutName, + &rDocument, + false, + false); + } + else + { + // Find first slide that uses the master page. + SdPage* pSlide = nullptr; + sal_uInt16 nPageCount = rDocument.GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage=0; nPage<nPageCount&&pSlide==nullptr; nPage++) + { + SdrPage* pCandidate = rDocument.GetSdPage(nPage,PageKind::Standard); + if (pCandidate != nullptr + && pCandidate->TRG_HasMasterPage() + && &(pCandidate->TRG_GetMasterPage()) == pPage) + { + pSlide = static_cast<SdPage*>(pCandidate); + } + } + + if (pSlide != nullptr) + { + // 2. Assign the given master pages to the first slide that was + // found above that uses the master page. + rDocument.SetMasterPage ( + (pSlide->GetPageNum()-1)/2, + rsBaseLayoutName, + &rDocument, + false, + false); + } + else + { + // 3. Replace the master page A by a copy of the given master + // page B. + rDocument.RemoveUnnecessaryMasterPages ( + pPage); + } + } +} + +SdPage* DocumentHelper::ProvideMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) +{ + // Make sure that both the master page and its notes master exist + // in the source document. If one is missing then return without + // making any changes. + if (pMasterPage == nullptr) + { + // The caller should make sure that the master page is valid. + OSL_ASSERT(pMasterPage != nullptr); + return nullptr; + } + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + SdPage* pNotesMasterPage = static_cast<SdPage*>( + rSourceDocument.GetMasterPage(pMasterPage->GetPageNum()+1)); + if (pNotesMasterPage == nullptr) + { + // The model is not in a valid state. Maybe a new master page + // is being (not finished yet) created? Return without making + // any changes. + return nullptr; + } + + SdPage* pMasterPageInDocument = nullptr; + // Search for a master page with the same name as the given one in + // the target document. + const OUString sMasterPageLayoutName (pMasterPage->GetLayoutName()); + for (sal_uInt16 nIndex=0,nCount=rTargetDocument.GetMasterPageCount(); nIndex<nCount; ++nIndex) + { + SdPage* pCandidate = static_cast<SdPage*>(rTargetDocument.GetMasterPage(nIndex)); + if (pCandidate && sMasterPageLayoutName == pCandidate->GetLayoutName()) + { + // The requested master page does already exist in the + // target document, return it. + return pCandidate; + } + } + + // The given master page does not already belong to the target + // document so we have to create copies and insert them into the + // target document. + + // Determine the position where the new master pages are inserted. + // By default they are inserted at the end. When we assign to a + // master page then insert after the last of the (selected) pages. + sal_uInt16 nInsertionIndex = rTargetDocument.GetMasterPageCount(); + if (rpPageList->front()->IsMasterPage()) + { + nInsertionIndex = rpPageList->back()->GetPageNum(); + } + + // Clone the master page. + if (&pMasterPage->getSdrModelFromSdrPage() != &rTargetDocument) + { + pMasterPageInDocument = AddMasterPage (rTargetDocument, pMasterPage, nInsertionIndex); + if( rTargetDocument.IsUndoEnabled() ) + rTargetDocument.AddUndo( + rTargetDocument.GetSdrUndoFactory().CreateUndoNewPage(*pMasterPageInDocument)); + } + else + pMasterPageInDocument = pMasterPage; + + // Clone the notes master. + if (&pNotesMasterPage->getSdrModelFromSdrPage() != &rTargetDocument) + { + SdPage* pClonedNotesMasterPage + = AddMasterPage (rTargetDocument, pNotesMasterPage, nInsertionIndex+1); + if( rTargetDocument.IsUndoEnabled() ) + rTargetDocument.AddUndo( + rTargetDocument.GetSdrUndoFactory().CreateUndoNewPage(*pClonedNotesMasterPage)); + } + + return pMasterPageInDocument; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/DocumentHelper.hxx b/sd/source/ui/sidebar/DocumentHelper.hxx new file mode 100644 index 000000000..61ba5f810 --- /dev/null +++ b/sd/source/ui/sidebar/DocumentHelper.hxx @@ -0,0 +1,108 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> +#include <sal/types.h> + +#include <memory> +#include <string_view> +#include <vector> + +class SdDrawDocument; +class SdPage; + +namespace sd::sidebar { + +/** A collection of methods supporting the handling of master pages. +*/ +class DocumentHelper +{ +public: + /** Return a copy of the given master page in the given document. + */ + static SdPage* CopyMasterPageToLocalDocument ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage); + + /** Return and, when not yet present, create a slide that uses the given + master page. + */ + static SdPage* GetSlideForMasterPage (SdPage const * pMasterPage); + + /** Copy the styles used by the given page from the source document to + the target document. + */ + static void ProvideStyles ( + SdDrawDocument const & rSourceDocument, + SdDrawDocument& rTargetDocument, + SdPage const * pPage); + + /** Assign the given master page to the list of pages. + @param rTargetDocument + The document that is the owner of the pages in rPageList. + @param pMasterPage + This master page will usually be a member of the list of all + available master pages as provided by the MasterPageContainer. + @param rPageList + The pages to which to assign the master page. These pages may + be slides or master pages themselves. + */ + static void AssignMasterPageToPageList ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rPageList); + +private: + static SdPage* AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage); + static SdPage* AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage, + sal_uInt16 nInsertionIndex); + static SdPage* ProvideMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList); + + /** Assign the given master page to the given page. + @param pMasterPage + In contrast to AssignMasterPageToPageList() this page is assumed + to be in the target document, i.e. the same document that pPage + is in. The caller will usually call AddMasterPage() to create a + clone of a master page in another document to create it. + @param rsBaseLayoutName + The layout name of the given master page. It is given so that + it has not to be created on every call. It could be generated + from the given master page, though. + @param pPage + The page to which to assign the master page. It can be a slide + or a master page itself. + */ + static void AssignMasterPageToPage ( + SdPage const * pMasterPage, + std::u16string_view rsBaseLayoutName, + SdPage* pPage); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/IDisposable.hxx b/sd/source/ui/sidebar/IDisposable.hxx new file mode 100644 index 000000000..e2c1afe27 --- /dev/null +++ b/sd/source/ui/sidebar/IDisposable.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ +#pragma once + +namespace sd::sidebar +{ +class IDisposable +{ +public: + virtual ~IDisposable(); +}; + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/ISidebarReceiver.hxx b/sd/source/ui/sidebar/ISidebarReceiver.hxx new file mode 100644 index 000000000..bf51cbe12 --- /dev/null +++ b/sd/source/ui/sidebar/ISidebarReceiver.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ +#pragma once + +namespace sd::sidebar +{ +class ISidebarReceiver +{ +public: + virtual ~ISidebarReceiver(); +}; + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/LayoutMenu.cxx b/sd/source/ui/sidebar/LayoutMenu.cxx new file mode 100644 index 000000000..23521df0e --- /dev/null +++ b/sd/source/ui/sidebar/LayoutMenu.cxx @@ -0,0 +1,728 @@ +/* -*- 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 "LayoutMenu.hxx" + +#include <app.hrc> +#include <drawdoc.hxx> +#include <framework/FrameworkHelper.hxx> +#include <strings.hrc> +#include <helpids.h> +#include <pres.hxx> +#include <sdmod.hxx> + +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <bitmaps.hlst> +#include <tools/gen.hxx> +#include <tools/SlotStateListener.hxx> +#include <DrawController.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <EventMultiplexer.hxx> +#include <SlideSorterViewShell.hxx> +#include <ViewShellBase.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <sal/log.hxx> + +#include <comphelper/processfactory.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/image.hxx> +#include <xmloff/autolayout.hxx> + +#include <com/sun/star/drawing/framework/XControllerManager.hpp> +#include <com/sun/star/drawing/framework/XView.hpp> +#include <com/sun/star/drawing/framework/ResourceId.hpp> + +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using namespace ::sd::slidesorter; +using ::sd::framework::FrameworkHelper; + +namespace sd::sidebar { + +namespace { + +struct snew_slide_value_info +{ + rtl::OUStringConstExpr msBmpResId; + TranslateId mpStrResId; + WritingMode meWritingMode; + AutoLayout maAutoLayout; +}; + +} + +constexpr OUStringLiteral EMPTY = u""; + +const snew_slide_value_info notes[] = +{ + {BMP_SLIDEN_01, STR_AUTOLAYOUT_NOTES, WritingMode_LR_TB, + AUTOLAYOUT_NOTES}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info handout[] = +{ + {BMP_SLIDEH_01, STR_AUTOLAYOUT_HANDOUT1, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT1}, + {BMP_SLIDEH_02, STR_AUTOLAYOUT_HANDOUT2, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT2}, + {BMP_SLIDEH_03, STR_AUTOLAYOUT_HANDOUT3, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT3}, + {BMP_SLIDEH_04, STR_AUTOLAYOUT_HANDOUT4, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT4}, + {BMP_SLIDEH_06, STR_AUTOLAYOUT_HANDOUT6, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT6}, + {BMP_SLIDEH_09, STR_AUTOLAYOUT_HANDOUT9, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT9}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info standard[] = +{ + {BMP_LAYOUT_EMPTY, STR_AUTOLAYOUT_NONE, WritingMode_LR_TB, AUTOLAYOUT_NONE}, + {BMP_LAYOUT_HEAD03, STR_AUTOLAYOUT_TITLE, WritingMode_LR_TB, AUTOLAYOUT_TITLE}, + {BMP_LAYOUT_HEAD02, STR_AUTOLAYOUT_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT}, + {BMP_LAYOUT_HEAD02A, STR_AUTOLAYOUT_2CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT}, + {BMP_LAYOUT_HEAD01, STR_AUTOLAYOUT_ONLY_TITLE, WritingMode_LR_TB, AUTOLAYOUT_TITLE_ONLY}, + {BMP_LAYOUT_TEXTONLY, STR_AUTOLAYOUT_ONLY_TEXT, WritingMode_LR_TB, AUTOLAYOUT_ONLY_TEXT}, + {BMP_LAYOUT_HEAD03B, STR_AUTOLAYOUT_2CONTENT_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT_CONTENT}, + {BMP_LAYOUT_HEAD03C, STR_AUTOLAYOUT_CONTENT_2CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT_2CONTENT}, + {BMP_LAYOUT_HEAD03A, STR_AUTOLAYOUT_2CONTENT_OVER_CONTENT,WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT}, + {BMP_LAYOUT_HEAD02B, STR_AUTOLAYOUT_CONTENT_OVER_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT}, + {BMP_LAYOUT_HEAD04, STR_AUTOLAYOUT_4CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_4CONTENT}, + {BMP_LAYOUT_HEAD06, STR_AUTOLAYOUT_6CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_6CONTENT}, + + // vertical + {BMP_LAYOUT_VERTICAL02, STR_AL_VERT_TITLE_TEXT_CHART, WritingMode_TB_RL, AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT}, + {BMP_LAYOUT_VERTICAL01, STR_AL_VERT_TITLE_VERT_OUTLINE, WritingMode_TB_RL, AUTOLAYOUT_VTITLE_VCONTENT}, + {BMP_LAYOUT_HEAD02, STR_AL_TITLE_VERT_OUTLINE, WritingMode_TB_RL, AUTOLAYOUT_TITLE_VCONTENT}, + {BMP_LAYOUT_HEAD02A, STR_AL_TITLE_VERT_OUTLINE_CLIPART, WritingMode_TB_RL, AUTOLAYOUT_TITLE_2VTEXT}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE} +}; + +class LayoutValueSet : public ValueSet +{ +private: + LayoutMenu& mrMenu; + + /** Calculate the number of displayed rows. This depends on the given + item size, the given number of columns, and the size of the + control. Note that this is not the number of rows managed by the + valueset. This number may be larger. In that case a vertical + scroll bar is displayed. + */ + int CalculateRowCount(const Size& rItemSize, int nColumnCount); + +public: + LayoutValueSet(LayoutMenu& rMenu) + : ValueSet(nullptr) + , mrMenu(rMenu) + { + } + + virtual void Resize() override; + + virtual bool Command(const CommandEvent& rEvent) override; +}; + +LayoutMenu::LayoutMenu ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : PanelLayout( pParent, "LayoutPanel", "modules/simpress/ui/layoutpanel.ui" ), + mrBase(rViewShellBase), + mxLayoutValueSet(new LayoutValueSet(*this)), + mxLayoutValueSetWin(new weld::CustomWeld(*m_xBuilder, "layoutvalueset", *mxLayoutValueSet)), + mbIsMainViewChangePending(false), + mxSidebar(rxSidebar), + mbIsDisposed(false) +{ + implConstruct( *mrBase.GetDocument()->GetDocSh() ); + SAL_INFO("sd.ui", "created LayoutMenu at " << this); + + mxLayoutValueSet->SetStyle(mxLayoutValueSet->GetStyle() | WB_ITEMBORDER | WB_FLATVALUESET | WB_TABSTOP); + + mxLayoutValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); +} + +void LayoutMenu::implConstruct( DrawDocShell& rDocumentShell ) +{ + OSL_ENSURE( mrBase.GetDocument()->GetDocSh() == &rDocumentShell, + "LayoutMenu::implConstruct: hmm?" ); + // if this fires, then my assumption that the rDocumentShell parameter to our first ctor is superfluous ... + (void) rDocumentShell; + + mxLayoutValueSet->SetStyle ( + ( mxLayoutValueSet->GetStyle() & ~(WB_ITEMBORDER) ) + | WB_TABSTOP + | WB_MENUSTYLEVALUESET + | WB_NO_DIRECTSELECT + ); + mxLayoutValueSet->SetExtraSpacing(2); + mxLayoutValueSet->SetSelectHdl (LINK(this, LayoutMenu, ClickHandler)); + InvalidateContent(); + + Link<::sd::tools::EventMultiplexerEvent&,void> aEventListenerLink (LINK(this,LayoutMenu,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->AddEventListener(aEventListenerLink); + + mxLayoutValueSet->SetHelpId(HID_SD_TASK_PANE_PREVIEW_LAYOUTS); + mxLayoutValueSet->SetAccessibleName(SdResId(STR_TASKPANEL_LAYOUT_MENU_TITLE)); + + Link<const OUString&,void> aStateChangeLink (LINK(this,LayoutMenu,StateChangeHandler)); + mxListener = new ::sd::tools::SlotStateListener( + aStateChangeLink, + Reference<frame::XDispatchProvider>(mrBase.GetController()->getFrame(), UNO_QUERY), + ".uno:VerticalTextState"); +} + +LayoutMenu::~LayoutMenu() +{ + SAL_INFO("sd.ui", "destroying LayoutMenu at " << this); + Dispose(); + mxLayoutValueSetWin.reset(); + mxLayoutValueSet.reset(); +} + +void LayoutMenu::Dispose() +{ + if (mbIsDisposed) + return; + + SAL_INFO("sd.ui", "disposing LayoutMenu at " << this); + + mbIsDisposed = true; + + Reference<lang::XComponent> xComponent (mxListener, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + Clear(); + Link<tools::EventMultiplexerEvent&,void> aLink (LINK(this,LayoutMenu,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->RemoveEventListener (aLink); +} + +AutoLayout LayoutMenu::GetSelectedAutoLayout() const +{ + AutoLayout aResult = AUTOLAYOUT_NONE; + + if (!mxLayoutValueSet->IsNoSelection() && mxLayoutValueSet->GetSelectedItemId()!=0) + { + AutoLayout* pLayout = static_cast<AutoLayout*>(mxLayoutValueSet->GetItemData(mxLayoutValueSet->GetSelectedItemId())); + if (pLayout != nullptr) + aResult = *pLayout; + } + + return aResult; +} + +ui::LayoutSize LayoutMenu::GetHeightForWidth (const sal_Int32 nWidth) +{ + sal_Int32 nPreferredHeight = 200; + if (mxLayoutValueSet->GetItemCount()>0) + { + Image aImage = mxLayoutValueSet->GetItemImage(mxLayoutValueSet->GetItemId(0)); + Size aItemSize = mxLayoutValueSet->CalcItemSizePixel(aImage.GetSizePixel()); + if (nWidth>0 && aItemSize.Width()>0) + { + aItemSize.AdjustWidth(8 ); + aItemSize.AdjustHeight(8 ); + int nColumnCount = nWidth / aItemSize.Width(); + if (nColumnCount <= 0) + nColumnCount = 1; + else if (nColumnCount > 4) + nColumnCount = 4; + int nRowCount = (mxLayoutValueSet->GetItemCount() + nColumnCount-1) / nColumnCount; + nPreferredHeight = nRowCount * aItemSize.Height(); + } + } + return ui::LayoutSize(nPreferredHeight,nPreferredHeight,nPreferredHeight); +} + +void LayoutValueSet::Resize() +{ + Size aWindowSize = GetOutputSizePixel(); + if (IsVisible() && aWindowSize.Width() > 0) + { + // Calculate the number of rows and columns. + if (GetItemCount() > 0) + { + Image aImage = GetItemImage(GetItemId(0)); + Size aItemSize = CalcItemSizePixel ( + aImage.GetSizePixel()); + aItemSize.AdjustWidth(8 ); + aItemSize.AdjustHeight(8 ); + int nColumnCount = aWindowSize.Width() / aItemSize.Width(); + if (nColumnCount < 1) + nColumnCount = 1; + else if (nColumnCount > 4) + nColumnCount = 4; + + int nRowCount = CalculateRowCount (aItemSize, nColumnCount); + + SetColCount(nColumnCount); + SetLineCount(nRowCount); + } + } + + ValueSet::Resize(); +} + +bool LayoutValueSet::Command(const CommandEvent& rEvent) +{ + if (rEvent.GetCommand() != CommandEventId::ContextMenu) + return false; + + // As a preparation for the context menu the item under the mouse is + // selected. + if (rEvent.IsMouseEvent()) + { + sal_uInt16 nIndex = GetItemId(rEvent.GetMousePosPixel()); + if (nIndex > 0) + SelectItem(nIndex); + } + + mrMenu.ShowContextMenu(rEvent.IsMouseEvent() ? &rEvent.GetMousePosPixel() : nullptr); + return true; +} + +void LayoutMenu::InsertPageWithLayout (AutoLayout aLayout) +{ + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + return; + + SfxViewFrame* pViewFrame = mrBase.GetViewFrame(); + if (pViewFrame == nullptr) + return; + + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if (pDispatcher == nullptr) + return; + + // Call SID_INSERTPAGE with the right arguments. This is because + // the popup menu can not call this slot with arguments directly. + SfxRequest aRequest (CreateRequest(SID_INSERTPAGE, aLayout)); + if (aRequest.GetArgs() != nullptr) + { + pDispatcher->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + *aRequest.GetArgs()); + } + UpdateSelection(); +} + +void LayoutMenu::InvalidateContent() +{ + // Throw away the current set and fill the menu anew according to the + // current settings (this includes the support for vertical writing.) + Fill(); + + if (mxSidebar.is()) + mxSidebar->requestLayout(); + + // set selection inside the control during Impress start up + UpdateSelection(); +} + +int LayoutValueSet::CalculateRowCount (const Size&, int nColumnCount) +{ + int nRowCount = 0; + + if (GetItemCount() > 0 && nColumnCount > 0) + { + nRowCount = (GetItemCount() + nColumnCount - 1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + } + + return nRowCount; +} + +IMPL_LINK_NOARG(LayoutMenu, ClickHandler, ValueSet*, void) +{ + AssignLayoutToSelectedSlides( GetSelectedAutoLayout() ); +} + +/** The specified layout is assigned to the current page of the view shell + in the center pane. +*/ +void LayoutMenu::AssignLayoutToSelectedSlides (AutoLayout aLayout) +{ + using namespace ::sd::slidesorter; + using namespace ::sd::slidesorter::controller; + + do + { + // The view shell in the center pane has to be present. + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell == nullptr) + break; + + // Determine if the current view is in an invalid master page mode. + // The handout view is always in master page mode and therefore not + // invalid. + bool bMasterPageMode (false); + switch (pMainViewShell->GetShellType()) + { + case ViewShell::ST_NOTES: + case ViewShell::ST_IMPRESS: + { + DrawViewShell* pDrawViewShell = static_cast<DrawViewShell*>(pMainViewShell); + if (pDrawViewShell->GetEditMode() == EditMode::MasterPage) + bMasterPageMode = true; + break; + } + default: + break; + } + if (bMasterPageMode) + break; + + // Get a list of all selected slides and call the SID_MODIFYPAGE + // slot for all of them. + ::sd::slidesorter::SharedPageSelection pPageSelection; + + // Get a list of selected pages. + // First we try to obtain this list from a slide sorter. This is + // possible only some of the view shells in the center pane. When + // no valid slide sorter is available then ask the main view shell + // for its current page. + SlideSorterViewShell* pSlideSorter = nullptr; + switch (pMainViewShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_SLIDE_SORTER: + pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase); + break; + default: + break; + } + if (pSlideSorter != nullptr) + { + // There is a slide sorter visible so get the list of selected pages from it. + pPageSelection = pSlideSorter->GetPageSelection(); + } + + if( (pSlideSorter == nullptr) || !pPageSelection || pPageSelection->empty() ) + { + // No valid slide sorter available. Ask the main view shell for + // its current page. + pPageSelection = std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + pPageSelection->push_back(pMainViewShell->GetActualPage()); + } + + if (pPageSelection->empty()) + break; + + for (const auto& rpPage : *pPageSelection) + { + if (rpPage == nullptr) + continue; + + // Call the SID_ASSIGN_LAYOUT slot with all the necessary parameters. + SfxRequest aRequest (mrBase.GetViewFrame(), SID_ASSIGN_LAYOUT); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATPAGE, (rpPage->GetPageNum()-1)/2)); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, aLayout)); + pMainViewShell->ExecuteSlot (aRequest, false); + } + } + while(false); +} + +SfxRequest LayoutMenu::CreateRequest ( + sal_uInt16 nSlotId, + AutoLayout aLayout) +{ + SfxRequest aRequest (mrBase.GetViewFrame(), nSlotId); + + do + { + SdrLayerAdmin& rLayerAdmin (mrBase.GetDocument()->GetLayerAdmin()); + SdrLayerID aBackground (rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + SdrLayerID aBackgroundObject (rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + break; + SdPage* pPage = pViewShell->GetActualPage(); + if (pPage == nullptr) + break; + + SdrLayerIDSet aVisibleLayers (pPage->TRG_GetMasterPageVisibleLayers()); + + aRequest.AppendItem( + SfxStringItem (ID_VAL_PAGENAME, OUString()));//pPage->GetName())); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, aLayout)); + aRequest.AppendItem( + SfxBoolItem(ID_VAL_ISPAGEBACK, aVisibleLayers.IsSet(aBackground))); + aRequest.AppendItem( + SfxBoolItem( + ID_VAL_ISPAGEOBJ, + aVisibleLayers.IsSet(aBackgroundObject))); + } + while (false); + + return aRequest; +} + +void LayoutMenu::Fill() +{ + bool bVertical = SvtCJKOptions::IsVerticalTextEnabled(); + SdDrawDocument* pDocument = mrBase.GetDocument(); + bool bRightToLeft = (pDocument!=nullptr + && pDocument->GetDefaultWritingMode() == WritingMode_RL_TB); + + // Get URL of the view in the center pane. + OUString sCenterPaneViewName; + try + { + Reference<XControllerManager> xControllerManager ( + Reference<XWeak>(&mrBase.GetDrawController()), UNO_QUERY_THROW); + Reference<XResourceId> xPaneId (ResourceId::create( + ::comphelper::getProcessComponentContext(), + FrameworkHelper::msCenterPaneURL)); + Reference<XView> xView (FrameworkHelper::Instance(mrBase)->GetView(xPaneId)); + if (xView.is()) + sCenterPaneViewName = xView->getResourceId()->getResourceURL(); + } + catch (RuntimeException&) + {} + + const snew_slide_value_info* pInfo = nullptr; + if (sCenterPaneViewName == framework::FrameworkHelper::msNotesViewURL) + { + pInfo = notes; + } + else if (sCenterPaneViewName == framework::FrameworkHelper::msHandoutViewURL) + { + pInfo = handout; + } + else if (sCenterPaneViewName == framework::FrameworkHelper::msImpressViewURL + || sCenterPaneViewName == framework::FrameworkHelper::msSlideSorterURL) + { + pInfo = standard; + } + else + { + pInfo = nullptr; + } + + Clear(); + for (sal_uInt16 i=1; pInfo!=nullptr && pInfo->mpStrResId; i++, pInfo++) + { + if ((WritingMode_TB_RL != pInfo->meWritingMode) || bVertical) + { + Image aImg("private:graphicrepository/" + static_cast<const OUString &>(pInfo->msBmpResId)); + + if (bRightToLeft && (WritingMode_TB_RL != pInfo->meWritingMode)) + { // FIXME: avoid interpolating RTL layouts. + BitmapEx aRTL = aImg.GetBitmapEx(); + aRTL.Mirror(BmpMirrorFlags::Horizontal); + aImg = Image(aRTL); + } + + mxLayoutValueSet->InsertItem(i, aImg, SdResId(pInfo->mpStrResId)); + mxLayoutValueSet->SetItemData (i, new AutoLayout(pInfo->maAutoLayout)); + } + } +} + +void LayoutMenu::Clear() +{ + for (size_t nId=1; nId<=mxLayoutValueSet->GetItemCount(); nId++) + delete static_cast<AutoLayout*>(mxLayoutValueSet->GetItemData(nId)); + mxLayoutValueSet->Clear(); +} + +void LayoutMenu::ShowContextMenu(const Point* pPos) +{ + if (SD_MOD()->GetWaterCan()) + return; + + // Determine the position where to show the menu. + Point aMenuPosition; + if (pPos) + { + auto nItemId = mxLayoutValueSet->GetItemId(*pPos); + if (nItemId <= 0) + return; + mxLayoutValueSet->SelectItem(nItemId); + aMenuPosition = *pPos; + } + else + { + if (mxLayoutValueSet->GetSelectedItemId() == sal_uInt16(-1)) + return; + ::tools::Rectangle aBBox(mxLayoutValueSet->GetItemRect(mxLayoutValueSet->GetSelectedItemId())); + aMenuPosition = aBBox.Center(); + } + + // Setup the menu. + ::tools::Rectangle aRect(aMenuPosition, Size(1, 1)); + weld::Widget* pPopupParent = mxLayoutValueSet->GetDrawingArea(); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/simpress/ui/layoutmenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + + // Disable the SID_INSERTPAGE_LAYOUT_MENU item when + // the document is read-only. + const SfxPoolItem* pItem = nullptr; + const SfxItemState aState ( + mrBase.GetViewFrame()->GetDispatcher()->QueryState(SID_INSERTPAGE, pItem)); + if (aState == SfxItemState::DISABLED) + xMenu->set_sensitive("insert", false); + + // Show the menu. + OnMenuItemSelected(xMenu->popup_at_rect(pPopupParent, aRect)); +} + +IMPL_LINK_NOARG(LayoutMenu, StateChangeHandler, const OUString&, void) +{ + InvalidateContent(); +} + +void LayoutMenu::OnMenuItemSelected(std::string_view ident) +{ + if (ident.empty()) + return; + + if (ident == "apply") + { + AssignLayoutToSelectedSlides(GetSelectedAutoLayout()); + } + else if (ident == "insert") + { + // Add arguments to this slot and forward it to the main view + // shell. + InsertPageWithLayout(GetSelectedAutoLayout()); + } +} + +// Selects an appropriate layout of the slide inside control. +// +// Method may be called several times with the same item-id to be selected - +// only once the actually state of the control will be changed. +// +void LayoutMenu::UpdateSelection() +{ + bool bItemSelected = false; + + do + { + // Get current page of main view. + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + break; + + SdPage* pCurrentPage = pViewShell->getCurrentPage(); + if (pCurrentPage == nullptr) + break; + + // Get layout of current page. + AutoLayout aLayout (pCurrentPage->GetAutoLayout()); + if (aLayout<AUTOLAYOUT_START || aLayout>AUTOLAYOUT_END) + break; + + // Find the entry of the menu for to the layout. + const sal_uInt16 nItemCount = mxLayoutValueSet->GetItemCount(); + for (sal_uInt16 nId=1; nId<=nItemCount; nId++) + { + if (*static_cast<AutoLayout*>(mxLayoutValueSet->GetItemData(nId)) == aLayout) + { + // do not set selection twice to the same item + if (mxLayoutValueSet->GetSelectedItemId() != nId) + { + mxLayoutValueSet->SetNoSelection(); + mxLayoutValueSet->SelectItem(nId); + } + + bItemSelected = true; // no need to call SetNoSelection() + break; + } + } + } + while (false); + + if (!bItemSelected) + mxLayoutValueSet->SetNoSelection(); +} + +IMPL_LINK(LayoutMenu, EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + // tdf#89890 During changes of the Layout of the slide when focus is not set inside main area + // we do not receive notification EventMultiplexerEventId::CurrentPageChanged, but we receive the following 3 notification types. + // => let's make UpdateSelection() also when some shape is changed (during Layout changes) + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + UpdateSelection(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::SlideSortedSelection: + UpdateSelection(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxLayoutValueSet->Invalidate(); // redraw without focus + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + InvalidateContent(); + } + break; + + default: + break; + } +} + +void LayoutMenu::DataChanged(const DataChangedEvent& rEvent) +{ + PanelLayout::DataChanged(rEvent); + Fill(); + mxLayoutValueSet->StyleUpdated(); + mxLayoutValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); +} + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/LayoutMenu.hxx b/sd/source/ui/sidebar/LayoutMenu.hxx new file mode 100644 index 000000000..4cc916858 --- /dev/null +++ b/sd/source/ui/sidebar/LayoutMenu.hxx @@ -0,0 +1,157 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <svtools/valueset.hxx> +#include <sfx2/request.hxx> +#include <xmloff/autolayout.hxx> + +namespace com::sun::star::frame +{ +class XStatusListener; +} +namespace com::sun::star::ui +{ +class XSidebar; +} + +namespace sd +{ +class DrawDocShell; +class ViewShellBase; +} + +namespace sd::tools +{ +class EventMultiplexerEvent; +} + +namespace sd::sidebar +{ +class LayoutValueSet; + +class LayoutMenu : public PanelLayout, public sfx2::sidebar::ILayoutableWindow +{ +public: + /** Create a new layout menu. Depending on the given flag it + displays its own scroll bar or lets a surrounding window + handle that. + @param i_pParent + the parent node in the control tree + @param i_rPanelViewShell + the view shell of the task pane. + */ + LayoutMenu(weld::Widget* pParent, ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~LayoutMenu() override; + + void Dispose(); + + /** Return a numerical value representing the currently selected + layout. + */ + AutoLayout GetSelectedAutoLayout() const; + + // From ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth(const sal_Int32 nWidth) override; + + /** Call this method when the set of displayed layouts is not up-to-date + anymore. It will re-assemble this set according to the current + settings. + */ + void InvalidateContent(); + + /** The context menu is requested over this ShowContextMenu() method. + */ + void ShowContextMenu(const Point* pPos); + + /** Call Fill() when switching to or from high contrast mode so that the + correct set of icons is displayed. + */ + virtual void DataChanged(const DataChangedEvent& rEvent) override; + +private: + ViewShellBase& mrBase; + + std::unique_ptr<LayoutValueSet> mxLayoutValueSet; + std::unique_ptr<weld::CustomWeld> mxLayoutValueSetWin; + + /** If we are asked for the preferred window size, then use this + many columns for the calculation. + */ + css::uno::Reference<css::frame::XStatusListener> mxListener; + bool mbIsMainViewChangePending; + css::uno::Reference<css::ui::XSidebar> mxSidebar; + bool mbIsDisposed; + + /** Fill the value set with the layouts that are applicable to the + current main view shell. + */ + void Fill(); + + /** Remove all items from the value set. + */ + void Clear(); + + /** Assign the given layout to all selected slides of a slide sorter. + If no slide sorter is active then this call is ignored. The slide + sorter in the center pane is preferred if the choice exists. + */ + void AssignLayoutToSelectedSlides(AutoLayout aLayout); + + /** Insert a new page with the given layout. The page is inserted via + the main view shell, i.e. its SID_INSERTPAGE slot is called. If it + does not support this slot then inserting a new page does not take + place. The new page is inserted after the currently active one (the + one returned by ViewShell::GetActualPage().) + */ + void InsertPageWithLayout(AutoLayout aLayout); + + /** Create a request structure that can be used with the SID_INSERTPAGE + and SID_MODIFYPAGE slots. The parameters are set so that the given + layout is assigned to the current page of the main view shell. + @param nSlotId + Supported slots are SID_INSERTPAGE and SID_MODIFYPAGE. + @param aLayout + Layout of the page to insert or to assign. + */ + SfxRequest CreateRequest(sal_uInt16 nSlotId, AutoLayout aLayout); + + /** Select the layout that is used by the current page. + */ + void UpdateSelection(); + + // internal ctor + void implConstruct(DrawDocShell& rDocumentShell); + + /** When clicked then set the current page of the view in the center pane. + */ + DECL_LINK(ClickHandler, ValueSet*, void); + DECL_LINK(StateChangeHandler, const OUString&, void); + DECL_LINK(EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, void); + void OnMenuItemSelected(std::string_view ident); +}; + +} // end of namespace ::sd::toolpanel + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainer.cxx b/sd/source/ui/sidebar/MasterPageContainer.cxx new file mode 100644 index 000000000..20d852807 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainer.cxx @@ -0,0 +1,958 @@ +/* -*- 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 "MasterPageContainer.hxx" + +#include "MasterPageContainerProviders.hxx" +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerFiller.hxx" +#include "MasterPageContainerQueue.hxx" +#include <PreviewRenderer.hxx> +#include <tools/SdGlobalResourceContainer.hxx> +#include <strings.hrc> +#include <algorithm> +#include <memory> + +#include <unomodel.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <comphelper/processfactory.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <tools/TimerBasedTaskExecution.hxx> +#include <o3tl/safeint.hxx> +#include <osl/mutex.hxx> +#include <osl/getglobalmutex.hxx> +#include <xmloff/autolayout.hxx> +#include <tools/debug.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +typedef ::std::vector<sd::sidebar::SharedMasterPageDescriptor> MasterPageContainerType; + +} // end of anonymous namespace + +namespace sd::sidebar { + +/** Inner implementation class of the MasterPageContainer. +*/ +class MasterPageContainer::Implementation + : public SdGlobalResource, + public MasterPageContainerFiller::ContainerAdapter, + public MasterPageContainerQueue::ContainerAdapter +{ +public: + mutable ::osl::Mutex maMutex; + + static std::weak_ptr<Implementation> mpInstance; + MasterPageContainerType maContainer; + + static std::shared_ptr<Implementation> Instance(); + + void LateInit(); + void AddChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink); + void RemoveChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink); + void UpdatePreviewSizePixel(); + const Size& GetPreviewSizePixel (PreviewSize eSize) const; + + bool HasToken (Token aToken) const; + SharedMasterPageDescriptor GetDescriptor (MasterPageContainer::Token aToken) const; + virtual Token PutMasterPage (const SharedMasterPageDescriptor& rDescriptor) override; + void InvalidatePreview (Token aToken); + Image GetPreviewForToken ( + Token aToken, + PreviewSize ePreviewSize); + PreviewState GetPreviewState (Token aToken) const; + bool RequestPreview (Token aToken); + + Reference<frame::XModel> GetModel(); + SdDrawDocument* GetDocument(); + + void FireContainerChange ( + MasterPageContainerChangeEvent::EventType eType, + Token aToken); + + virtual bool UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) override; + + void ReleaseDescriptor (Token aToken); + + /** Called by the MasterPageContainerFiller to notify that all master + pages from template documents have been added. + */ + virtual void FillingDone() override; + +private: + Implementation(); + virtual ~Implementation() override; + + class Deleter { public: + void operator() (Implementation* pObject) { delete pObject; } + }; + friend class Deleter; + + enum class InitializationState { NotInitialized, Initializing, Initialized }; + InitializationState meInitializationState; + + std::unique_ptr<MasterPageContainerQueue> mpRequestQueue; + css::uno::Reference<css::frame::XModel> mxModel; + SdDrawDocument* mpDocument; + PreviewRenderer maPreviewRenderer; + /** Remember whether the first page object has already been used to + determine the correct size ratio. + */ + bool mbFirstPageObjectSeen; + + // The widths for the previews contain two pixels for the border that is + // painted around the preview. + static const int SMALL_PREVIEW_WIDTH = 72 + 2; + static const int LARGE_PREVIEW_WIDTH = 2*72 + 2; + + /** This substitution of page preview shows "Preparing preview" and is + shown as long as the actual previews are not being present. + */ + Image maLargePreviewBeingCreated; + Image maSmallPreviewBeingCreated; + + /** This substitution of page preview is shown when a preview can not be + created and thus is not available. + */ + Image maLargePreviewNotAvailable; + Image maSmallPreviewNotAvailable; + + ::std::vector<Link<MasterPageContainerChangeEvent&,void>> maChangeListeners; + + // We have to remember the tasks for initialization and filling in case + // a MasterPageContainer object is destroyed before these tasks have + // been completed. + std::weak_ptr<sd::tools::TimerBasedTaskExecution> mpFillerTask; + + Size maSmallPreviewSizePixel; + Size maLargePreviewSizePixel; + + Image GetPreviewSubstitution(TranslateId pId, PreviewSize ePreviewSize); + + void CleanContainer(); +}; + +//===== MasterPageContainer =================================================== + +std::weak_ptr<MasterPageContainer::Implementation> + MasterPageContainer::Implementation::mpInstance; + +std::shared_ptr<MasterPageContainer::Implementation> + MasterPageContainer::Implementation::Instance() +{ + std::shared_ptr<MasterPageContainer::Implementation> pInstance; + + if (Implementation::mpInstance.expired()) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (Implementation::mpInstance.expired()) + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pInstance = std::shared_ptr<MasterPageContainer::Implementation>( + new MasterPageContainer::Implementation(), + MasterPageContainer::Implementation::Deleter()); + SdGlobalResourceContainer::Instance().AddResource(pInstance); + Implementation::mpInstance = pInstance; + } + else + pInstance = std::shared_ptr<MasterPageContainer::Implementation>( + Implementation::mpInstance); + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pInstance = std::shared_ptr<MasterPageContainer::Implementation>( + Implementation::mpInstance); + } + + DBG_ASSERT(pInstance != nullptr, + "MasterPageContainer::Implementation::Instance(): instance is nullptr"); + return pInstance; +} + +MasterPageContainer::MasterPageContainer() + : mpImpl(Implementation::Instance()), + mePreviewSize(SMALL) +{ + mpImpl->LateInit(); +} + +MasterPageContainer::~MasterPageContainer() +{ +} + +void MasterPageContainer::AddChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + mpImpl->AddChangeListener(rLink); +} + +void MasterPageContainer::RemoveChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + mpImpl->RemoveChangeListener(rLink); +} + +void MasterPageContainer::SetPreviewSize (PreviewSize eSize) +{ + mePreviewSize = eSize; + mpImpl->FireContainerChange( + MasterPageContainerChangeEvent::EventType::SIZE_CHANGED, + NIL_TOKEN); +} + +Size const & MasterPageContainer::GetPreviewSizePixel() const +{ + return mpImpl->GetPreviewSizePixel(mePreviewSize); +} + +MasterPageContainer::Token MasterPageContainer::PutMasterPage ( + const std::shared_ptr<MasterPageDescriptor>& rDescriptor) +{ + return mpImpl->PutMasterPage(rDescriptor); +} + +void MasterPageContainer::AcquireToken (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + { + ++pDescriptor->mnUseCount; + } +} + +void MasterPageContainer::ReleaseToken (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (!pDescriptor) + return; + + OSL_ASSERT(pDescriptor->mnUseCount>0); + --pDescriptor->mnUseCount; + if (pDescriptor->mnUseCount > 0) + return; + + switch (pDescriptor->meOrigin) + { + case DEFAULT: + case TEMPLATE: + default: + break; + + case MASTERPAGE: + mpImpl->ReleaseDescriptor(aToken); + break; + } +} + +int MasterPageContainer::GetTokenCount() const +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->maContainer.size(); +} + +bool MasterPageContainer::HasToken (Token aToken) const +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->HasToken(aToken); +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForIndex (int nIndex) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (HasToken(nIndex)) + aResult = mpImpl->maContainer[nIndex]->maToken; + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForURL ( + const OUString& sURL) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (!sURL.isEmpty()) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::URLComparator(sURL))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForStyleName (const OUString& sStyleName) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (!sStyleName.isEmpty()) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::StyleNameComparator(sStyleName))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForPageObject ( + const SdPage* pPage) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (pPage != nullptr) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::PageObjectComparator(pPage))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +OUString MasterPageContainer::GetURLForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msURL; + else + return OUString(); +} + +OUString MasterPageContainer::GetPageNameForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msPageName; + else + return OUString(); +} + +OUString MasterPageContainer::GetStyleNameForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msStyleName; + else + return OUString(); +} + +SdPage* MasterPageContainer::GetPageObjectForToken ( + MasterPageContainer::Token aToken, + bool bLoad) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SdPage* pPageObject = nullptr; + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + { + pPageObject = pDescriptor->mpMasterPage; + if (pPageObject == nullptr) + { + // The page object is not (yet) present. Call + // UpdateDescriptor() to trigger the PageObjectProvider() to + // provide it. + if (bLoad) + mpImpl->GetModel(); + if (mpImpl->UpdateDescriptor(pDescriptor,bLoad,false, true)) + pPageObject = pDescriptor->mpMasterPage; + } + } + return pPageObject; +} + +MasterPageContainer::Origin MasterPageContainer::GetOriginForToken (Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->meOrigin; + else + return UNKNOWN; +} + +sal_Int32 MasterPageContainer::GetTemplateIndexForToken (Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->mnTemplateIndex; + else + return -1; +} + +std::shared_ptr<MasterPageDescriptor> MasterPageContainer::GetDescriptorForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->GetDescriptor(aToken); +} + +void MasterPageContainer::InvalidatePreview (MasterPageContainer::Token aToken) +{ + mpImpl->InvalidatePreview(aToken); +} + +Image MasterPageContainer::GetPreviewForToken (MasterPageContainer::Token aToken) +{ + return mpImpl->GetPreviewForToken(aToken,mePreviewSize); +} + +MasterPageContainer::PreviewState MasterPageContainer::GetPreviewState (Token aToken) +{ + return mpImpl->GetPreviewState(aToken); +} + +bool MasterPageContainer::RequestPreview (Token aToken) +{ + return mpImpl->RequestPreview(aToken); +} + +//==== Implementation ================================================ + +MasterPageContainer::Implementation::Implementation() + : meInitializationState(InitializationState::NotInitialized), + mpDocument(nullptr), + mbFirstPageObjectSeen(false) +{ + UpdatePreviewSizePixel(); +} + +MasterPageContainer::Implementation::~Implementation() +{ + // When the initializer or filler tasks are still running then we have + // to stop them now in order to prevent them from calling us back. + tools::TimerBasedTaskExecution::ReleaseTask(mpFillerTask); + + mpRequestQueue.reset(); + + uno::Reference<util::XCloseable> xCloseable (mxModel, uno::UNO_QUERY); + if (xCloseable.is()) + { + try + { + xCloseable->close(true); + } + catch (const css::util::CloseVetoException&) + { + } + } + mxModel = nullptr; +} + +void MasterPageContainer::Implementation::LateInit() +{ + const ::osl::MutexGuard aGuard (maMutex); + + if (meInitializationState != InitializationState::NotInitialized) + return; + + meInitializationState = InitializationState::Initializing; + + OSL_ASSERT(Instance().get()==this); + mpRequestQueue.reset(MasterPageContainerQueue::Create( + std::shared_ptr<MasterPageContainerQueue::ContainerAdapter>(Instance()))); + + mpFillerTask = ::sd::tools::TimerBasedTaskExecution::Create( + std::make_shared<MasterPageContainerFiller>(*this), + 5, + 50); + + meInitializationState = InitializationState::Initialized; +} + +void MasterPageContainer::Implementation::AddChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ::std::vector<Link<MasterPageContainerChangeEvent&,void>>::iterator iListener ( + ::std::find(maChangeListeners.begin(),maChangeListeners.end(),rLink)); + if (iListener == maChangeListeners.end()) + maChangeListeners.push_back(rLink); + +} + +void MasterPageContainer::Implementation::RemoveChangeListener (const Link<MasterPageContainerChangeEvent&,void>& rLink) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ::std::vector<Link<MasterPageContainerChangeEvent&,void>>::iterator iListener ( + ::std::find(maChangeListeners.begin(),maChangeListeners.end(),rLink)); + if (iListener != maChangeListeners.end()) + maChangeListeners.erase(iListener); +} + +void MasterPageContainer::Implementation::UpdatePreviewSizePixel() +{ + const ::osl::MutexGuard aGuard (maMutex); + + // The default aspect ratio is 4:3 + int nWidth (4); + int nHeight (3); + + // Search for the first entry with an existing master page. + auto iDescriptor = std::find_if(maContainer.begin(), maContainer.end(), + [](const SharedMasterPageDescriptor& rxDescriptor) { + return rxDescriptor != nullptr && rxDescriptor->mpMasterPage != nullptr; + }); + if (iDescriptor != maContainer.end()) + { + Size aPageSize ((*iDescriptor)->mpMasterPage->GetSize()); + OSL_ASSERT(!aPageSize.IsEmpty()); + if (aPageSize.Width() > 0) + nWidth = aPageSize.Width(); + if (aPageSize.Height() > 0) + nHeight = aPageSize.Height(); + mbFirstPageObjectSeen = true; + } + + maSmallPreviewSizePixel.setWidth( SMALL_PREVIEW_WIDTH ); + maLargePreviewSizePixel.setWidth( LARGE_PREVIEW_WIDTH ); + + int nNewSmallHeight ((maSmallPreviewSizePixel.Width()-2) * nHeight / nWidth + 2); + int nNewLargeHeight ((maLargePreviewSizePixel.Width()-2) * nHeight / nWidth + 2); + + if (nNewSmallHeight!=maSmallPreviewSizePixel.Height() + || nNewLargeHeight!=maLargePreviewSizePixel.Height()) + { + maSmallPreviewSizePixel.setHeight( nNewSmallHeight ); + maLargePreviewSizePixel.setHeight( nNewLargeHeight ); + FireContainerChange( + MasterPageContainerChangeEvent::EventType::SIZE_CHANGED, + NIL_TOKEN); + } +} + +const Size& MasterPageContainer::Implementation::GetPreviewSizePixel (PreviewSize eSize) const +{ + if (eSize == SMALL) + return maSmallPreviewSizePixel; + else + return maLargePreviewSizePixel; +} + +MasterPageContainer::Token MasterPageContainer::Implementation::PutMasterPage ( + const SharedMasterPageDescriptor& rpDescriptor) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Token aResult (NIL_TOKEN); + + // Get page object and preview when that is inexpensive. + UpdateDescriptor(rpDescriptor,false,false, false); + + // Look up the new MasterPageDescriptor and either insert it or update + // an already existing one. + MasterPageContainerType::iterator aEntry ( + ::std::find_if ( + maContainer.begin(), + maContainer.end(), + MasterPageDescriptor::AllComparator(rpDescriptor))); + if (aEntry == maContainer.end()) + { + // Insert a new MasterPageDescriptor. + bool bIgnore(rpDescriptor->mpPageObjectProvider == nullptr + && rpDescriptor->msURL.isEmpty()); + + if ( ! bIgnore) + { + CleanContainer(); + + aResult = maContainer.size(); + rpDescriptor->SetToken(aResult); + + // Templates are precious, i.e. we lock them so that they will + // not be destroyed when (temporarily) no one references them. + // They will only be deleted when the container is destroyed. + switch (rpDescriptor->meOrigin) + { + case TEMPLATE: + case DEFAULT: + ++rpDescriptor->mnUseCount; + break; + + default: + break; + } + + maContainer.push_back(rpDescriptor); + aEntry = maContainer.end()-1; + + FireContainerChange(MasterPageContainerChangeEvent::EventType::CHILD_ADDED,aResult); + } + } + else + { + // Update an existing MasterPageDescriptor. + aResult = (*aEntry)->maToken; + std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > pEventTypes( + (*aEntry)->Update(*rpDescriptor)); + if (pEventTypes != nullptr && !pEventTypes->empty()) + { + // One or more aspects of the descriptor have changed. Send + // appropriate events to the listeners. + UpdateDescriptor(*aEntry,false,false, true); + + for (const auto& rEventType : *pEventTypes) + { + FireContainerChange(rEventType, (*aEntry)->maToken); + } + } + } + + return aResult; +} + +bool MasterPageContainer::Implementation::HasToken (Token aToken) const +{ + return aToken>=0 + && o3tl::make_unsigned(aToken)<maContainer.size() + && maContainer[aToken]; +} + +SharedMasterPageDescriptor MasterPageContainer::Implementation::GetDescriptor (Token aToken) const +{ + if (aToken>=0 && o3tl::make_unsigned(aToken)<maContainer.size()) + return maContainer[aToken]; + else + return SharedMasterPageDescriptor(); +} + +void MasterPageContainer::Implementation::InvalidatePreview (Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + SharedMasterPageDescriptor pDescriptor (GetDescriptor(aToken)); + if (pDescriptor) + { + pDescriptor->maSmallPreview = Image(); + pDescriptor->maLargePreview = Image(); + RequestPreview(aToken); + } +} + +Image MasterPageContainer::Implementation::GetPreviewForToken ( + MasterPageContainer::Token aToken, + PreviewSize ePreviewSize) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Image aPreview; + PreviewState ePreviewState (GetPreviewState(aToken)); + + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + + // When the preview is missing but inexpensively creatable then do that + // now. + if (pDescriptor) + { + if (ePreviewState == PS_CREATABLE) + if (UpdateDescriptor(pDescriptor, false,false, true)) + if (pDescriptor->maLargePreview.GetSizePixel().Width() != 0) + ePreviewState = PS_AVAILABLE; + + switch (ePreviewState) + { + case PS_AVAILABLE: + aPreview = pDescriptor->GetPreview(ePreviewSize); + break; + + case PS_PREPARING: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION, + ePreviewSize); + break; + + case PS_CREATABLE: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION, + ePreviewSize); + break; + + case PS_NOT_AVAILABLE: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION, + ePreviewSize); + if (ePreviewSize == SMALL) + pDescriptor->maSmallPreview = aPreview; + else + pDescriptor->maLargePreview = aPreview; + break; + } + } + + return aPreview; +} + +MasterPageContainer::PreviewState MasterPageContainer::Implementation::GetPreviewState ( + Token aToken) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + PreviewState eState (PS_NOT_AVAILABLE); + + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + if (pDescriptor) + { + if (pDescriptor->maLargePreview.GetSizePixel().Width() != 0) + eState = PS_AVAILABLE; + else if (pDescriptor->mpPreviewProvider != nullptr) + { + // The preview does not exist but can be created. When that is + // not expensive then do it at once. + if (mpRequestQueue->HasRequest(aToken)) + eState = PS_PREPARING; + else + eState = PS_CREATABLE; + } + else + eState = PS_NOT_AVAILABLE; + } + + return eState; +} + +bool MasterPageContainer::Implementation::RequestPreview (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + if (pDescriptor) + return mpRequestQueue->RequestPreview(pDescriptor); + else + return false; +} + +Reference<frame::XModel> MasterPageContainer::Implementation::GetModel() +{ + const ::osl::MutexGuard aGuard (maMutex); + + if ( ! mxModel.is()) + { + // Create a new model. + mxModel.set( + ::comphelper::getProcessServiceFactory()->createInstance( + "com.sun.star.presentation.PresentationDocument"), + uno::UNO_QUERY); + + // Initialize the model. + uno::Reference<frame::XLoadable> xLoadable (mxModel,uno::UNO_QUERY); + if (xLoadable.is()) + xLoadable->initNew(); + + // Use its tunnel to get a pointer to its core implementation. + uno::Reference<lang::XUnoTunnel> xUnoTunnel (mxModel, uno::UNO_QUERY); + if (auto pSdXImpressDocument = comphelper::getFromUnoTunnel<SdXImpressDocument>(xUnoTunnel)) + { + mpDocument = pSdXImpressDocument->GetDoc(); + } + + // Create a default page. + uno::Reference<drawing::XDrawPagesSupplier> xSlideSupplier (mxModel, uno::UNO_QUERY); + if (xSlideSupplier.is()) + { + uno::Reference<drawing::XDrawPages> xSlides = + xSlideSupplier->getDrawPages(); + if (xSlides.is()) + { + uno::Reference<drawing::XDrawPage> xNewPage (xSlides->insertNewByIndex(0)); + uno::Reference<beans::XPropertySet> xProperties(xNewPage, uno::UNO_QUERY); + if (xProperties.is()) + xProperties->setPropertyValue( + "Layout", + Any(sal_Int16(AUTOLAYOUT_TITLE))); + } + } + } + return mxModel; +} + +SdDrawDocument* MasterPageContainer::Implementation::GetDocument() +{ + GetModel(); + return mpDocument; +} + +Image MasterPageContainer::Implementation::GetPreviewSubstitution ( + TranslateId pId, + PreviewSize ePreviewSize) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Image aPreview; + + if (pId == STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION) + { + Image& rPreview (ePreviewSize==SMALL + ? maSmallPreviewBeingCreated + : maLargePreviewBeingCreated); + if (rPreview.GetSizePixel().Width() == 0) + { + rPreview = maPreviewRenderer.RenderSubstitution( + ePreviewSize==SMALL ? maSmallPreviewSizePixel : maLargePreviewSizePixel, + SdResId(STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION)); + } + aPreview = rPreview; + } + else if (pId == STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION) + { + Image& rPreview (ePreviewSize==SMALL + ? maSmallPreviewNotAvailable + : maLargePreviewNotAvailable); + if (rPreview.GetSizePixel().Width() == 0) + { + rPreview = maPreviewRenderer.RenderSubstitution( + ePreviewSize==SMALL ? maSmallPreviewSizePixel : maLargePreviewSizePixel, + SdResId(STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION)); + } + aPreview = rPreview; + } + + return aPreview; +} + +void MasterPageContainer::Implementation::CleanContainer() +{ + // Remove the empty elements at the end of the container. The empty + // elements in the middle can not be removed because that would + // invalidate the references still held by others. + int nIndex (maContainer.size()-1); + while (nIndex>=0 && !maContainer[nIndex]) + --nIndex; + maContainer.resize(++nIndex); +} + +void MasterPageContainer::Implementation::FireContainerChange ( + MasterPageContainerChangeEvent::EventType eType, + Token aToken) +{ + ::std::vector<Link<MasterPageContainerChangeEvent&,void>> aCopy(maChangeListeners); + MasterPageContainerChangeEvent aEvent; + aEvent.meEventType = eType; + aEvent.maChildToken = aToken; + for (const auto& rListener : aCopy) + rListener.Call(aEvent); +} + +bool MasterPageContainer::Implementation::UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) +{ + const ::osl::MutexGuard aGuard (maMutex); + + // We have to create the page object when the preview provider needs it + // and the caller needs the preview. + bForcePageObject |= (bForcePreview + && rpDescriptor->mpPreviewProvider->NeedsPageObject() + && rpDescriptor->mpMasterPage==nullptr); + + // Define a cost threshold so that an update or page object or preview + // that is at least this cost are made at once. Updates with higher cost + // are scheduled for later. + sal_Int32 nCostThreshold (mpRequestQueue->IsEmpty() ? 5 : 0); + + // Update the page object (which may be used for the preview update). + if (bForcePageObject) + GetDocument(); + int nPageObjectModified (rpDescriptor->UpdatePageObject( + (bForcePageObject ? -1 : nCostThreshold), + mpDocument)); + if (nPageObjectModified == 1 && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::DATA_CHANGED, + rpDescriptor->maToken); + if (nPageObjectModified == -1 && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::CHILD_REMOVED, + rpDescriptor->maToken); + if (nPageObjectModified && ! mbFirstPageObjectSeen) + UpdatePreviewSizePixel(); + + // Update the preview. + bool bPreviewModified (rpDescriptor->UpdatePreview( + (bForcePreview ? -1 : nCostThreshold), + maSmallPreviewSizePixel, + maLargePreviewSizePixel, + maPreviewRenderer)); + + if (bPreviewModified && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED, + rpDescriptor->maToken); + + return nPageObjectModified || bPreviewModified; +} + +void MasterPageContainer::Implementation::ReleaseDescriptor (Token aToken) +{ + if (aToken>=0 && o3tl::make_unsigned(aToken)<maContainer.size()) + { + maContainer[aToken].reset(); + } +} + +void MasterPageContainer::Implementation::FillingDone() +{ + mpRequestQueue->ProcessAllRequests(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainer.hxx b/sd/source/ui/sidebar/MasterPageContainer.hxx new file mode 100644 index 000000000..9de4eb6bc --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainer.hxx @@ -0,0 +1,199 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/image.hxx> + +#include <memory> + +class SdPage; + +namespace sd::sidebar +{ +class MasterPageDescriptor; +class MasterPageContainerChangeEvent; + +/** This container manages the master pages used by the MasterPagesSelector + controls. It uses internally a singleton implementation object. + Therefore, all MasterPageContainer object operator on the same set of + master pages. Each MasterPageContainer, however, has its own + PreviewSize value and thus can independently switch between large and + small previews. + + The container maintains its own document to store master page objects. + + For each master page container stores its URL, preview bitmap, page + name, and, if available, the page object. + + Entries are accessed via a Token, which is mostly a numerical index but + whose values do not necessarily have to be consecutive. +*/ +class MasterPageContainer final +{ +public: + typedef int Token; + static const Token NIL_TOKEN = -1; + + MasterPageContainer(); + ~MasterPageContainer(); + + void AddChangeListener(const Link<MasterPageContainerChangeEvent&, void>& rLink); + void RemoveChangeListener(const Link<MasterPageContainerChangeEvent&, void>& rLink); + + enum PreviewSize + { + SMALL, + LARGE + }; + /** There are two different preview sizes, a small one and a large one. + Which one is used by the called container can be changed with this + method. + When the preview size is changed then all change listeners are + notified of this. + */ + void SetPreviewSize(PreviewSize eSize); + + /** Returns the preview size. + */ + PreviewSize GetPreviewSize() const { return mePreviewSize; } + + /** Return the preview size in pixels. + */ + Size const& GetPreviewSizePixel() const; + + enum PreviewState + { + PS_AVAILABLE, + PS_CREATABLE, + PS_PREPARING, + PS_NOT_AVAILABLE + }; + PreviewState GetPreviewState(Token aToken); + + /** This method is typically called for entries in the container for + which GetPreviewState() returns OS_CREATABLE. The creation of the + preview is then scheduled to be executed asynchronously at a later + point in time. When the preview is available the change listeners + will be notified. + */ + bool RequestPreview(Token aToken); + + /** Each entry of the container is either the first page of a template + document or is a master page of an Impress document. + */ + enum Origin + { + MASTERPAGE, // Master page of a document. + TEMPLATE, // First page of a template file. + DEFAULT, // Empty master page with default style. + UNKNOWN + }; + + /** Put the master page identified and described by the given parameters + into the container. When there already is a master page with the + given URL, page name, or object pointer (when that is not NULL) then + the existing entry is replaced/updated by the given one. Otherwise + a new entry is inserted. + */ + Token PutMasterPage(const std::shared_ptr<MasterPageDescriptor>& rDescriptor); + void AcquireToken(Token aToken); + void ReleaseToken(Token aToken); + + /** This and the GetTokenForIndex() methods can be used to iterate over + all members of the container. + */ + int GetTokenCount() const; + + /** Determine whether the container has a member for the given token. + */ + bool HasToken(Token aToken) const; + + /** Return a token for an index in the range + 0 <= index < GetTokenCount(). + */ + Token GetTokenForIndex(int nIndex); + + Token GetTokenForURL(const OUString& sURL); + Token GetTokenForStyleName(const OUString& sStyleName); + Token GetTokenForPageObject(const SdPage* pPage); + + OUString GetURLForToken(Token aToken); + OUString GetPageNameForToken(Token aToken); + OUString GetStyleNameForToken(Token aToken); + SdPage* GetPageObjectForToken(Token aToken, bool bLoad); + Origin GetOriginForToken(Token aToken); + sal_Int32 GetTemplateIndexForToken(Token aToken); + std::shared_ptr<MasterPageDescriptor> GetDescriptorForToken(Token aToken); + + void InvalidatePreview(Token aToken); + + /** Return a preview for the specified token. When the preview is not + present then the PreviewProvider associated with the token is + executed only when that is not expensive. It is the responsibility + of the caller to call RequestPreview() to do the same + (asynchronously) for expensive PreviewProviders. + Call GetPreviewState() to find out if that is necessary. + @param aToken + This token specifies for which master page to return the preview. + Tokens are returned for example by the GetTokenFor...() methods. + @return + The returned image is the requested preview or a substitution. + */ + Image GetPreviewForToken(Token aToken); + +private: + class Implementation; + std::shared_ptr<Implementation> mpImpl; + PreviewSize mePreviewSize; +}; + +/** For some changes to the set of master pages in a MasterPageContainer or + to the data stored for each master page one or more events are sent to + registered listeners. + Each event has an event type and a token that tells the listener where + the change took place. +*/ +class MasterPageContainerChangeEvent +{ +public: + enum class EventType + { + // A master page was added to the container. + CHILD_ADDED, + // A master page was removed from the container. + CHILD_REMOVED, + // The preview of a master page has changed. + PREVIEW_CHANGED, + // The size of a preview has changed. + SIZE_CHANGED, + // Some of the data stored for a master page has changed. + DATA_CHANGED, + // The TemplateIndex of a master page has changed. + INDEX_CHANGED, + } meEventType; + + // Token of the container entry whose data changed or which was added or + // removed. + MasterPageContainer::Token maChildToken; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerFiller.cxx b/sd/source/ui/sidebar/MasterPageContainerFiller.cxx new file mode 100644 index 000000000..3568d9c71 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerFiller.cxx @@ -0,0 +1,168 @@ +/* -*- 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 "MasterPageContainerFiller.hxx" + +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerProviders.hxx" +#include <TemplateScanner.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::sidebar { + +MasterPageContainerFiller::MasterPageContainerFiller (ContainerAdapter& rpAdapter) + : mrContainerAdapter(rpAdapter), + meState(INITIALIZE_TEMPLATE_SCANNER), + mpLastAddedEntry(nullptr), + mnIndex(1) +{ + // Add one entry for the default master page. We use temporarily the + // DefaultPagePreviewProvider to prevent the rendering (and the + // expensive creation) of the default page. It is replaced later on by + // another. + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::DEFAULT, + 0, + OUString(), + OUString(), + OUString(), + false, + std::make_shared<DefaultPageObjectProvider>(), + std::make_shared<PagePreviewProvider>()); + mrContainerAdapter.PutMasterPage(pDescriptor); +} + +MasterPageContainerFiller::~MasterPageContainerFiller() +{ +} + +void MasterPageContainerFiller::RunNextStep() +{ + switch (meState) + { + case INITIALIZE_TEMPLATE_SCANNER: + mpScannerTask.reset(new TemplateScanner()); + meState = SCAN_TEMPLATE; + break; + + case SCAN_TEMPLATE: + meState = ScanTemplate(); + break; + + case ADD_TEMPLATE: + meState = AddTemplate(); + break; + + case DONE: + case ERROR: + default: + break; + } + + // When the state has just been set to DONE or ERROR then tell the + // container that no more templates will be coming and stop the + // scanning. + switch (meState) + { + case DONE: + case ERROR: + if (mpScannerTask != nullptr) + { + mrContainerAdapter.FillingDone(); + mpScannerTask.reset(); + } + break; + default: + break; + } +} + +bool MasterPageContainerFiller::HasNextStep() +{ + switch (meState) + { + case DONE: + case ERROR: + return false; + + default: + return true; + } +} + +MasterPageContainerFiller::State MasterPageContainerFiller::ScanTemplate() +{ + State eState (ERROR); + + if (mpScannerTask != nullptr) + { + if (mpScannerTask->HasNextStep()) + { + mpScannerTask->RunNextStep(); + if (mpScannerTask->GetLastAddedEntry() != mpLastAddedEntry) + { + mpLastAddedEntry = mpScannerTask->GetLastAddedEntry(); + if (mpLastAddedEntry != nullptr) + eState = ADD_TEMPLATE; + else + eState = SCAN_TEMPLATE; + } + else + eState = SCAN_TEMPLATE; + } + else + eState = DONE; + } + + return eState; +} + +MasterPageContainerFiller::State MasterPageContainerFiller::AddTemplate() +{ + if (mpLastAddedEntry != nullptr) + { + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::TEMPLATE, + mnIndex, + mpLastAddedEntry->msPath, + mpLastAddedEntry->msTitle, + OUString(), + false, + std::make_shared<TemplatePageObjectProvider>(mpLastAddedEntry->msPath), + std::make_shared<TemplatePreviewProvider>(mpLastAddedEntry->msPath)); + // For user supplied templates we use a different preview provider: + // The preview in the document shows not only shapes on the master + // page but also shapes on the foreground. This is misleading and + // therefore these previews are discarded and created directly from + // the page objects. + if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) + pDescriptor->mpPreviewProvider = std::make_shared<PagePreviewProvider>(); + + mrContainerAdapter.PutMasterPage(pDescriptor); + ++mnIndex; + } + + return SCAN_TEMPLATE; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerFiller.hxx b/sd/source/ui/sidebar/MasterPageContainerFiller.hxx new file mode 100644 index 000000000..b08452ab6 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerFiller.hxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" +#include <tools/AsynchronousTask.hxx> + +namespace sd +{ +class TemplateScanner; +class TemplateEntry; +} + +namespace sd::sidebar +{ +/** Fill a MasterPageContainer with information about the available master + pages. These are provided by one default page and from the existing + Impress templates. This is done asynchronously. +*/ +class MasterPageContainerFiller : public ::sd::tools::AsynchronousTask +{ +public: + class ContainerAdapter + { + public: + virtual MasterPageContainer::Token + PutMasterPage(const SharedMasterPageDescriptor& rpDescriptor) + = 0; + /** This method is called when all Impress templates have been added + to the container via the PutMasterPage() method. + */ + virtual void FillingDone() = 0; + + protected: + ~ContainerAdapter() {} + }; + + explicit MasterPageContainerFiller(ContainerAdapter& rContainerAdapter); + virtual ~MasterPageContainerFiller(); + + /** Run the next step of the task. After HasNextStep() returns false + this method should ignore further calls. + */ + virtual void RunNextStep() override; + + /** Return <TRUE/> when there is at least one more step to execute. + When the task has been executed completely then <FALSE/> is + returned. + */ + virtual bool HasNextStep() override; + +private: + ContainerAdapter& mrContainerAdapter; + // Remember what the next step has to do. + enum State + { + INITIALIZE_TEMPLATE_SCANNER, + SCAN_TEMPLATE, + ADD_TEMPLATE, + ERROR, + DONE + } meState; + ::std::unique_ptr<TemplateScanner> mpScannerTask; + const TemplateEntry* mpLastAddedEntry; + int mnIndex; + + State ScanTemplate(); + State AddTemplate(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerProviders.cxx b/sd/source/ui/sidebar/MasterPageContainerProviders.cxx new file mode 100644 index 000000000..785536daa --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerProviders.cxx @@ -0,0 +1,205 @@ +/* -*- 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 "MasterPageContainerProviders.hxx" + +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <PreviewRenderer.hxx> +#include <svl/eitem.hxx> +#include <sfx2/app.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/thumbnailview.hxx> +#include <vcl/image.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::sidebar { + +//===== PagePreviewProvider =================================================== + +PagePreviewProvider::PagePreviewProvider() +{ +} + +Image PagePreviewProvider::operator () ( + int nWidth, + SdPage* pPage, + ::sd::PreviewRenderer& rRenderer) +{ + Image aPreview; + + if (pPage != nullptr) + { + // Use the given renderer to create a preview of the given page + // object. + aPreview = rRenderer.RenderPage( + pPage, + nWidth); + } + + return aPreview; +} + +int PagePreviewProvider::GetCostIndex() +{ + return 5; +} + +bool PagePreviewProvider::NeedsPageObject() +{ + return true; +} + +//===== TemplatePreviewProvider =============================================== + +TemplatePreviewProvider::TemplatePreviewProvider (const OUString& rsURL) + : msURL(rsURL) +{ +} + +Image TemplatePreviewProvider::operator() ( + int, + SdPage*, + ::sd::PreviewRenderer&) +{ + return Image(ThumbnailView::readThumbnail(msURL)); +} + +int TemplatePreviewProvider::GetCostIndex() +{ + return 10; +} + +bool TemplatePreviewProvider::NeedsPageObject() +{ + return false; +} + +//===== TemplatePageObjectProvider ============================================= + +TemplatePageObjectProvider::TemplatePageObjectProvider (const OUString& rsURL) + : msURL(rsURL) +{ +} + +SdPage* TemplatePageObjectProvider::operator() (SdDrawDocument*) +{ + SdPage* pPage = nullptr; + + mxDocumentShell = nullptr; + try + { + // Load the template document and return its first page. + ::sd::DrawDocShell* pDocumentShell = LoadDocument (msURL); + if (pDocumentShell != nullptr) + { + SdDrawDocument* pDocument = pDocumentShell->GetDoc(); + if (pDocument != nullptr) + { + pPage = pDocument->GetMasterSdPage(0, PageKind::Standard); + // In order to make the newly loaded master page deletable + // when copied into documents it is marked as no "precious". + // When it is modified then it is marked as "precious". + if (pPage != nullptr) + pPage->SetPrecious(false); + } + } + } + catch (const uno::RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + pPage = nullptr; + } + + return pPage; +} + +::sd::DrawDocShell* TemplatePageObjectProvider::LoadDocument (const OUString& sFileName) +{ + SfxApplication* pSfxApp = SfxGetpApp(); + std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet (pSfxApp->GetPool())); + pSet->Put (SfxBoolItem (SID_TEMPLATE, true)); + pSet->Put (SfxBoolItem (SID_PREVIEW, true)); + if (pSfxApp->LoadTemplate (mxDocumentShell, sFileName, std::move(pSet))) + { + mxDocumentShell = nullptr; + } + SfxObjectShell* pShell = mxDocumentShell; + return dynamic_cast< ::sd::DrawDocShell *>( pShell ); +} + +int TemplatePageObjectProvider::GetCostIndex() +{ + return 20; +} + +//===== DefaultPageObjectProvider ============================================== + +DefaultPageObjectProvider::DefaultPageObjectProvider() +{ +} + +SdPage* DefaultPageObjectProvider::operator () (SdDrawDocument* pContainerDocument) +{ + SdPage* pLocalMasterPage = nullptr; + if (pContainerDocument != nullptr) + { + SdPage* pLocalSlide = pContainerDocument->GetSdPage(0, PageKind::Standard); + if (pLocalSlide!=nullptr && pLocalSlide->TRG_HasMasterPage()) + pLocalMasterPage = dynamic_cast<SdPage*>(&pLocalSlide->TRG_GetMasterPage()); + } + + if (pLocalMasterPage == nullptr) + { + SAL_WARN( "sd", "can not create master page for slide"); + } + + return pLocalMasterPage; +} + +int DefaultPageObjectProvider::GetCostIndex() +{ + return 15; +} + +//===== ExistingPageProvider ================================================== + +ExistingPageProvider::ExistingPageProvider (SdPage* pPage) + : mpPage(pPage) +{ +} + +SdPage* ExistingPageProvider::operator() (SdDrawDocument*) +{ + return mpPage; +} + +int ExistingPageProvider::GetCostIndex() +{ + return 0; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerProviders.hxx b/sd/source/ui/sidebar/MasterPageContainerProviders.hxx new file mode 100644 index 000000000..b76076e15 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerProviders.hxx @@ -0,0 +1,175 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <sfx2/objsh.hxx> + +class Image; +class SdDrawDocument; +class SdPage; +namespace sd +{ +class PreviewRenderer; +} +namespace sd +{ +class DrawDocShell; +} + +namespace sd::sidebar +{ +/** Interface for a provider of page objects. It is used by the + MasterPageDescriptor to create master page objects on demand. +*/ +class PageObjectProvider +{ +public: + /** Return a master page either by returning an already existing one, by + creating a new page, or by loading a document. + @param pDocument + The document of the MasterPageContainer. It may be used to + create new pages. + */ + virtual SdPage* operator()(SdDrawDocument* pDocument) = 0; + + /** An abstract value for the expected cost of providing a master page + object. + @return + A value of 0 represents for the lowest cost, i.e. an almost + immediate return. Positive values stand for higher costs. + Negative values are not supported. + */ + virtual int GetCostIndex() = 0; + +protected: + ~PageObjectProvider() {} +}; + +class PreviewProvider +{ +public: + /** Create a preview image in the specified width. + @param nWidth + Requested width of the preview. The calling method can cope + with other sizes as well but the resulting image quality is + better when the returned image has the requested size. + @param pPage + Page object for which a preview is requested. This may be NULL + when the page object is expensive to get and the PreviewProvider + does not need this object (NeedsPageObject() returns false.) + @param rRenderer + This PreviewRenderer may be used by the PreviewProvider to + create a preview image. + */ + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) = 0; + + /** Return a value that indicates how expensive the creation of a + preview image is. The higher the returned value the more expensive + is the preview creation. Return 0 when the preview is already + present and can be returned immediately. + */ + virtual int GetCostIndex() = 0; + + /** Return whether the page object passed is necessary to create a + preview. + */ + virtual bool NeedsPageObject() = 0; + +protected: + ~PreviewProvider() {} +}; + +/** Provide previews of existing page objects by rendering them. +*/ +class PagePreviewProvider : public PreviewProvider +{ +public: + PagePreviewProvider(); + virtual ~PagePreviewProvider() {} + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) override; + virtual int GetCostIndex() override; + virtual bool NeedsPageObject() override; + +private: +}; + +/** Provide master page objects for template documents for which only the + URL is given. +*/ +class TemplatePageObjectProvider : public PageObjectProvider +{ +public: + explicit TemplatePageObjectProvider(const OUString& rsURL); + virtual ~TemplatePageObjectProvider(){}; + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; + +private: + OUString msURL; + SfxObjectShellLock mxDocumentShell; + ::sd::DrawDocShell* LoadDocument(const OUString& sFileName); +}; + +/** Provide previews for template documents by loading the thumbnails from + the documents. +*/ +class TemplatePreviewProvider : public PreviewProvider +{ +public: + explicit TemplatePreviewProvider(const OUString& rsURL); + virtual ~TemplatePreviewProvider(){}; + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) override; + virtual int GetCostIndex() override; + virtual bool NeedsPageObject() override; + +private: + OUString msURL; +}; + +/** Create an empty default master page. +*/ +class DefaultPageObjectProvider : public PageObjectProvider +{ +public: + DefaultPageObjectProvider(); + virtual ~DefaultPageObjectProvider() {} + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; +}; + +/** This implementation of the PageObjectProvider simply returns an already + existing master page object. +*/ +class ExistingPageProvider : public PageObjectProvider +{ +public: + explicit ExistingPageProvider(SdPage* pPage); + virtual ~ExistingPageProvider() {} + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; + +private: + SdPage* mpPage; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerQueue.cxx b/sd/source/ui/sidebar/MasterPageContainerQueue.cxx new file mode 100644 index 000000000..229f3d972 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerQueue.cxx @@ -0,0 +1,263 @@ +/* -*- 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 "MasterPageContainerQueue.hxx" +#include "MasterPageContainerProviders.hxx" + +#include <tools/IdleDetection.hxx> + +#include <set> + +namespace sd::sidebar { + +const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeout (15); +const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeoutWhenNotIdle (100); +const sal_Int32 MasterPageContainerQueue::snMasterPagePriorityBoost (5); +const sal_Int32 MasterPageContainerQueue::snWaitForMoreRequestsPriorityThreshold (-10); +sal_uInt32 MasterPageContainerQueue::snWaitForMoreRequestsCount(15); + +//===== MasterPageContainerQueue::PreviewCreationRequest ====================== + +class MasterPageContainerQueue::PreviewCreationRequest +{ +public: + PreviewCreationRequest (const SharedMasterPageDescriptor& rpDescriptor, int nPriority) + : mpDescriptor(rpDescriptor), + mnPriority(nPriority) + {} + SharedMasterPageDescriptor mpDescriptor; + int mnPriority; + class Compare + { + public: + bool operator() (const PreviewCreationRequest& r1,const PreviewCreationRequest& r2) const + { + if (r1.mnPriority != r2.mnPriority) + { + // Prefer requests with higher priority. + return r1.mnPriority > r2.mnPriority; + } + else + { + // Prefer tokens that have been earlier created (those with lower + // value). + return r1.mpDescriptor->maToken < r2.mpDescriptor->maToken; + } + } + }; + class CompareToken + { + public: + MasterPageContainer::Token maToken; + explicit CompareToken(MasterPageContainer::Token aToken) : maToken(aToken) {} + bool operator() (const PreviewCreationRequest& rRequest) const + { return maToken==rRequest.mpDescriptor->maToken; } + }; +}; + +//===== MasterPageContainerQueue::RequestQueue ================================ + +class MasterPageContainerQueue::RequestQueue + : public ::std::set<PreviewCreationRequest,PreviewCreationRequest::Compare> +{ +public: + RequestQueue() {} +}; + +//===== MasterPageContainerQueue ============================================== + +MasterPageContainerQueue* MasterPageContainerQueue::Create ( + const std::weak_ptr<ContainerAdapter>& rpContainer) +{ + MasterPageContainerQueue* pQueue = new MasterPageContainerQueue(rpContainer); + pQueue->LateInit(); + return pQueue; +} + +MasterPageContainerQueue::MasterPageContainerQueue ( + const std::weak_ptr<ContainerAdapter>& rpContainer) + : mpWeakContainer(rpContainer), + mpRequestQueue(new RequestQueue()), + maDelayedPreviewCreationTimer("sd MasterPageContainerQueue maDelayedPreviewCreationTimer"), + mnRequestsServedCount(0) +{ +} + +MasterPageContainerQueue::~MasterPageContainerQueue() +{ + maDelayedPreviewCreationTimer.Stop(); + while ( ! mpRequestQueue->empty()) + mpRequestQueue->erase(mpRequestQueue->begin()); +} + +void MasterPageContainerQueue::LateInit() +{ + // Set up the timer for the delayed creation of preview bitmaps. + maDelayedPreviewCreationTimer.SetTimeout (snDelayedCreationTimeout); + maDelayedPreviewCreationTimer.SetInvokeHandler( + LINK(this,MasterPageContainerQueue,DelayedPreviewCreation) ); +} + +bool MasterPageContainerQueue::RequestPreview (const SharedMasterPageDescriptor& rpDescriptor) +{ + bool bSuccess (false); + if (rpDescriptor + && rpDescriptor->maLargePreview.GetSizePixel().Width() == 0) + { + sal_Int32 nPriority (CalculatePriority(rpDescriptor)); + + // Add a new or replace an existing request. + RequestQueue::iterator iRequest (::std::find_if( + mpRequestQueue->begin(), + mpRequestQueue->end(), + PreviewCreationRequest::CompareToken(rpDescriptor->maToken))); + // When a request for the same token exists then the lowest of the + // two priorities is used. + if (iRequest != mpRequestQueue->end()) + if (iRequest->mnPriority < nPriority) + { + mpRequestQueue->erase(iRequest); + iRequest = mpRequestQueue->end(); + } + + // Add a new request when none exists (or has just been erased). + if (iRequest == mpRequestQueue->end()) + { + mpRequestQueue->insert(PreviewCreationRequest(rpDescriptor,nPriority)); + maDelayedPreviewCreationTimer.Start(); + bSuccess = true; + } + } + return bSuccess; +} + +sal_Int32 MasterPageContainerQueue::CalculatePriority ( + const SharedMasterPageDescriptor& rpDescriptor) +{ + sal_Int32 nPriority; + + // The cost is used as a starting value. + int nCost (0); + if (rpDescriptor->mpPreviewProvider != nullptr) + { + nCost = rpDescriptor->mpPreviewProvider->GetCostIndex(); + if (rpDescriptor->mpPreviewProvider->NeedsPageObject()) + if (rpDescriptor->mpPageObjectProvider != nullptr) + nCost += rpDescriptor->mpPageObjectProvider->GetCostIndex(); + } + + // Its negative value is used so that requests with a low cost are + // preferred over those with high costs. + nPriority = -nCost; + + // Add a term that introduces an order based on the appearance in the + // AllMasterPagesSelector. + nPriority -= rpDescriptor->maToken / 3; + + // Process requests for the CurrentMasterPagesSelector first. + if (rpDescriptor->meOrigin == MasterPageContainer::MASTERPAGE) + nPriority += snMasterPagePriorityBoost; + + return nPriority; +} + +IMPL_LINK(MasterPageContainerQueue, DelayedPreviewCreation, Timer*, pTimer, void) +{ + bool bIsShowingFullScreenShow (false); + bool bWaitForMoreRequests (false); + + do + { + if (mpRequestQueue->empty()) + break; + + // First check whether the system is idle. + tools::IdleState nIdleState (tools::IdleDetection::GetIdleState(nullptr)); + if (nIdleState != tools::IdleState::Idle) + { + if (nIdleState & tools::IdleState::FullScreenShowActive) + bIsShowingFullScreenShow = true; + break; + } + + PreviewCreationRequest aRequest (*mpRequestQueue->begin()); + + // Check if the request should really be processed right now. + // Reasons to not do it are when its cost is high and not many other + // requests have been inserted into the queue that would otherwise + // be processed first. + if (aRequest.mnPriority < snWaitForMoreRequestsPriorityThreshold + && (mnRequestsServedCount+mpRequestQueue->size() < snWaitForMoreRequestsCount)) + { + // Wait for more requests before this one is processed. Note + // that the queue processing is not started anew when this + // method is left. That is done when the next request is + // inserted. + bWaitForMoreRequests = true; + break; + } + + mpRequestQueue->erase(mpRequestQueue->begin()); + + if (aRequest.mpDescriptor) + { + mnRequestsServedCount += 1; + if ( ! mpWeakContainer.expired()) + { + std::shared_ptr<ContainerAdapter> pContainer (mpWeakContainer); + if (pContainer != nullptr) + pContainer->UpdateDescriptor(aRequest.mpDescriptor,false,true,true); + } + } + } + while (false); + + if (!mpRequestQueue->empty() && ! bWaitForMoreRequests) + { + int nTimeout (snDelayedCreationTimeout); + if (bIsShowingFullScreenShow) + nTimeout = snDelayedCreationTimeoutWhenNotIdle; + maDelayedPreviewCreationTimer.SetTimeout(nTimeout); + pTimer->Start(); + } +} + +bool MasterPageContainerQueue::HasRequest (MasterPageContainer::Token aToken) const +{ + return std::any_of( + mpRequestQueue->begin(), + mpRequestQueue->end(), + PreviewCreationRequest::CompareToken(aToken)); +} + +bool MasterPageContainerQueue::IsEmpty() const +{ + return mpRequestQueue->empty(); +} + +void MasterPageContainerQueue::ProcessAllRequests() +{ + snWaitForMoreRequestsCount = 0; + if (!mpRequestQueue->empty()) + maDelayedPreviewCreationTimer.Start(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerQueue.hxx b/sd/source/ui/sidebar/MasterPageContainerQueue.hxx new file mode 100644 index 000000000..6b9b0adca --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerQueue.hxx @@ -0,0 +1,131 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" + +#include <vcl/timer.hxx> + +#include <memory> + +namespace sd::sidebar { + +/** The queue stores and processes all requests from a MasterPageContainer + for the creation of previews. + The order of request processing and its timing is controlled by a + heuristic that uses values given with each request and which is + controlled by various parameters that are described below. +*/ +class MasterPageContainerQueue final +{ +public: + class ContainerAdapter { + public: + virtual bool UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) = 0; + + protected: + ~ContainerAdapter() {} + }; + + static MasterPageContainerQueue* Create ( + const std::weak_ptr<ContainerAdapter>& rpContainer); + ~MasterPageContainerQueue(); + + /** This method is typically called for entries in the container for + which GetPreviewState() returns OS_CREATABLE. The creation of the + preview is then scheduled to be executed asynchronously at a later + point in time. When the preview is available the change listeners + will be notified. + */ + bool RequestPreview (const SharedMasterPageDescriptor& rDescriptor); + + /** Return <TRUE/> when there is a request currently in the queue for + the given token. + */ + bool HasRequest (MasterPageContainer::Token aToken) const; + + /** Return <TRUE/> when there is at least one request in the queue. + */ + bool IsEmpty() const; + + /** After this call the queue does not wait anymore for requests with + higher priority when only a small number of requests with lower + priority are present. This method should be called when all + templates are inserted into the MasterPageContainer. + */ + void ProcessAllRequests(); + +private: + std::weak_ptr<ContainerAdapter> mpWeakContainer; + class PreviewCreationRequest; + class RequestQueue; + std::unique_ptr<RequestQueue> mpRequestQueue; + Timer maDelayedPreviewCreationTimer; + sal_uInt32 mnRequestsServedCount; + + // There are a couple of values that define various aspects of the + // heuristic that defines the order and timing in which requests for + // preview creation are processed. + + /** The time to wait (in milliseconds) between the creation of previews. + */ + static const sal_Int32 snDelayedCreationTimeout; + + /** The time to wait when the system is not idle. + */ + static const sal_Int32 snDelayedCreationTimeoutWhenNotIdle; + + /** Requests for previews of master pages in a document have their + priority increased by this value. + */ + static const sal_Int32 snMasterPagePriorityBoost; + + /** When only requests which a priority lower than this threshold exist + and not many requests have been made yet then wait with processing + them until more requests are present. + */ + static const sal_Int32 snWaitForMoreRequestsPriorityThreshold; + + /** When only requests which a priority lower than a threshold exist + and not more requests than this number have been made or already + processed then wait with processing them until more requests are + present. + */ + static sal_uInt32 snWaitForMoreRequestsCount; + + explicit MasterPageContainerQueue (const std::weak_ptr<ContainerAdapter>& rpContainer); + void LateInit(); + + /** Calculate the priority that defines the order in which requests + are processed. + */ + static sal_Int32 CalculatePriority (const SharedMasterPageDescriptor& rDescriptor); + + DECL_LINK(DelayedPreviewCreation, Timer *, void); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageDescriptor.cxx b/sd/source/ui/sidebar/MasterPageDescriptor.cxx new file mode 100644 index 000000000..2c0c23eb7 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageDescriptor.cxx @@ -0,0 +1,341 @@ +/* -*- 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 <memory> +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerProviders.hxx" + +#include "DocumentHelper.hxx" +#include <PreviewRenderer.hxx> +#include <sdpage.hxx> +#include <tools/urlobj.hxx> +#include <sal/log.hxx> + +namespace sd::sidebar { + +//===== MasterPageDescriptor ================================================== + +MasterPageDescriptor::MasterPageDescriptor ( + MasterPageContainer::Origin eOrigin, + const sal_Int32 nTemplateIndex, + std::u16string_view rsURL, + const OUString& rsPageName, + const OUString& rsStyleName, + const bool bIsPrecious, + const std::shared_ptr<PageObjectProvider>& rpPageObjectProvider, + const std::shared_ptr<PreviewProvider>& rpPreviewProvider) + : maToken(MasterPageContainer::NIL_TOKEN), + meOrigin(eOrigin), + msURL(INetURLObject(rsURL).GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)), + msPageName(rsPageName), + msStyleName(rsStyleName), + mbIsPrecious(bIsPrecious), + mpMasterPage(nullptr), + mpSlide(nullptr), + mpPreviewProvider(rpPreviewProvider), + mpPageObjectProvider(rpPageObjectProvider), + mnTemplateIndex(nTemplateIndex), + meURLClassification(URLCLASS_UNDETERMINED), + mnUseCount(0) +{ +} + +void MasterPageDescriptor::SetToken (MasterPageContainer::Token aToken) +{ + maToken = aToken; +} + +const Image& MasterPageDescriptor::GetPreview (MasterPageContainer::PreviewSize eSize) const +{ + if (eSize == MasterPageContainer::SMALL) + return maSmallPreview; + else + return maLargePreview; +} + +::std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > + MasterPageDescriptor::Update ( + const MasterPageDescriptor& rDescriptor) +{ + bool bDataChanged (false); + bool bIndexChanged (false); + bool bPreviewChanged (false); + + if (meOrigin==MasterPageContainer::UNKNOWN + && rDescriptor.meOrigin!=MasterPageContainer::UNKNOWN) + { + meOrigin = rDescriptor.meOrigin; + bIndexChanged = true; + } + + if (msURL.isEmpty() && !rDescriptor.msURL.isEmpty()) + { + msURL = rDescriptor.msURL; + bDataChanged = true; + } + + if (msPageName.isEmpty() && !rDescriptor.msPageName.isEmpty()) + { + msPageName = rDescriptor.msPageName; + bDataChanged = true; + } + + if (msStyleName.isEmpty() && !rDescriptor.msStyleName.isEmpty()) + { + msStyleName = rDescriptor.msStyleName; + bDataChanged = true; + } + + if (mpPageObjectProvider == nullptr && rDescriptor.mpPageObjectProvider != nullptr) + { + mpPageObjectProvider = rDescriptor.mpPageObjectProvider; + bDataChanged = true; + } + + if (mpPreviewProvider == nullptr && rDescriptor.mpPreviewProvider != nullptr) + { + mpPreviewProvider = rDescriptor.mpPreviewProvider; + bPreviewChanged = true; + } + + if (mnTemplateIndex<0 && rDescriptor.mnTemplateIndex>=0) + { + mnTemplateIndex = rDescriptor.mnTemplateIndex; + bIndexChanged = true; + } + + // Prepare the list of event types that will be returned. + ::std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > pResult; + if (bDataChanged || bIndexChanged || bPreviewChanged) + { + pResult.reset(new std::vector<MasterPageContainerChangeEvent::EventType>); + if (bDataChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::DATA_CHANGED); + if (bIndexChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::INDEX_CHANGED); + if (bPreviewChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED); + } + + return pResult; +} + +int MasterPageDescriptor::UpdatePageObject ( + sal_Int32 nCostThreshold, + SdDrawDocument* pDocument) +{ + int nModified = 0; + + // Update the page object when that is not yet known. + if (mpMasterPage == nullptr && mpPageObjectProvider != nullptr + && (nCostThreshold < 0 || mpPageObjectProvider->GetCostIndex() <= nCostThreshold)) + { + // Note that pDocument may be NULL. + + SdPage* pPage = (*mpPageObjectProvider)(pDocument); + if (meOrigin == MasterPageContainer::MASTERPAGE) + { + mpMasterPage = pPage; + if (mpMasterPage != nullptr) + mpMasterPage->SetPrecious(mbIsPrecious); + } + else + { + // Master pages from templates are copied into the local document. + if (pDocument != nullptr) + mpMasterPage = DocumentHelper::CopyMasterPageToLocalDocument(*pDocument,pPage); + mpSlide = DocumentHelper::GetSlideForMasterPage(mpMasterPage); + } + + if (mpMasterPage != nullptr) + { + // Update page name and style name. + if (msPageName.isEmpty()) + msPageName = mpMasterPage->GetName(); + msStyleName = mpMasterPage->GetName(); + + // Delete an existing substitution. The next request for a preview + // will create the real one. + maSmallPreview = Image(); + maLargePreview = Image(); + mpPreviewProvider = std::make_shared<PagePreviewProvider>(); + } + else + { + SAL_WARN( "sd", "UpdatePageObject: master page is NULL"); + return -1; + } + + nModified = 1; + } + + return nModified; +} + +bool MasterPageDescriptor::UpdatePreview ( + sal_Int32 nCostThreshold, + const Size& rSmallSize, + const Size& rLargeSize, + ::sd::PreviewRenderer& rRenderer) +{ + bool bModified (false); + + // Update the preview when that is not yet known. + if (maLargePreview.GetSizePixel().Width() == 0 && mpPreviewProvider != nullptr + && (nCostThreshold < 0 || mpPreviewProvider->GetCostIndex() <= nCostThreshold)) + { + SdPage* pPage = mpSlide; + if (pPage == nullptr) + { + pPage = mpMasterPage; + } + //TODO: Notify LOOL of preview updates. + maLargePreview = (*mpPreviewProvider)( + rLargeSize.Width(), + pPage, + rRenderer); + if (maLargePreview.GetSizePixel().Width() > 0) + { + // Create the small preview by scaling the large one down. + maSmallPreview = rRenderer.ScaleBitmap( + maLargePreview.GetBitmapEx(), + rSmallSize.Width()); + // The large preview may not have the desired width. Scale it + // accordingly. + if (maLargePreview.GetSizePixel().Width() != rLargeSize.Width()) + maLargePreview = rRenderer.ScaleBitmap( + maLargePreview.GetBitmapEx(), + rLargeSize.Width()); + bModified = true; + } + } + + return bModified; +} + +MasterPageDescriptor::URLClassification MasterPageDescriptor::GetURLClassification() +{ + if (meURLClassification == URLCLASS_UNDETERMINED) + { + if (msURL.isEmpty()) + meURLClassification = URLCLASS_UNKNOWN; + else if (msURL.indexOf("presnt")>=0) + { + meURLClassification = URLCLASS_PRESENTATION; + } + else if (msURL.indexOf("layout")>=0) + { + meURLClassification = URLCLASS_LAYOUT; + } + else if (msURL.indexOf("educate")>=0) + { + meURLClassification = URLCLASS_OTHER; + } + else + { + meURLClassification = URLCLASS_USER; + } + } + + return meURLClassification; +} + +//===== URLComparator ========================================================= + +MasterPageDescriptor::URLComparator::URLComparator (const OUString& sURL) + : msURL(sURL) +{ +} + +bool MasterPageDescriptor::URLComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->msURL == msURL; +} + +// ===== StyleNameComparator ================================================== + +MasterPageDescriptor::StyleNameComparator::StyleNameComparator (const OUString& sStyleName) + : msStyleName(sStyleName) +{ +} + +bool MasterPageDescriptor::StyleNameComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->msStyleName == msStyleName; +} + +//===== PageObjectComparator ================================================== + +MasterPageDescriptor::PageObjectComparator::PageObjectComparator (const SdPage* pPageObject) + : mpMasterPage(pPageObject) +{ +} + +bool MasterPageDescriptor::PageObjectComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->mpMasterPage==mpMasterPage; +} + +//===== AllComparator ========================================================= + +MasterPageDescriptor::AllComparator::AllComparator(const SharedMasterPageDescriptor& rDescriptor) + : mpDescriptor(rDescriptor) +{ +} + +bool MasterPageDescriptor::AllComparator::operator() (const SharedMasterPageDescriptor&rDescriptor) +{ + if (!rDescriptor) + return false; + else + { + // Take URL, page name, style name, and page object into account + // when comparing two descriptors. When two descriptors are + // identical in any of these values then there are thought of as + // equivalent. Only the Origin has to be the same in both + // descriptors. + return mpDescriptor->meOrigin == rDescriptor->meOrigin + && ((!mpDescriptor->msURL.isEmpty() && mpDescriptor->msURL == rDescriptor->msURL) + || (!mpDescriptor->msPageName.isEmpty() + && mpDescriptor->msPageName == rDescriptor->msPageName) + || (!mpDescriptor->msStyleName.isEmpty() + && mpDescriptor->msStyleName == rDescriptor->msStyleName) + || (mpDescriptor->mpMasterPage != nullptr + && mpDescriptor->mpMasterPage == rDescriptor->mpMasterPage) + || (mpDescriptor->mpPageObjectProvider != nullptr + && rDescriptor->mpPageObjectProvider != nullptr + && mpDescriptor->mpPageObjectProvider == rDescriptor->mpPageObjectProvider)); + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageDescriptor.hxx b/sd/source/ui/sidebar/MasterPageDescriptor.hxx new file mode 100644 index 000000000..62717e528 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageDescriptor.hxx @@ -0,0 +1,231 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPageContainer.hxx" +#include <memory> + +namespace sd { class PreviewRenderer; } +class SdDrawDocument; + +namespace sd::sidebar { + +class PageObjectProvider; +class PreviewProvider; + +class MasterPageDescriptor; +typedef std::shared_ptr<MasterPageDescriptor> SharedMasterPageDescriptor; + +/** A collection of data that is stored for every master page in the + MasterpageContainer. +*/ +class MasterPageDescriptor +{ +public: + MasterPageDescriptor ( + MasterPageContainer::Origin eOrigin, + const sal_Int32 nTemplateIndex, + std::u16string_view rURL, + const OUString& rPageName, + const OUString& rStyleName, + const bool bIsPrecious, + const std::shared_ptr<PageObjectProvider>& rpPageObjectProvider, + const std::shared_ptr<PreviewProvider>& rpPreviewProvider); + + void SetToken (MasterPageContainer::Token aToken); + + /** Update the called MasterPageDescriptor object with values from the + given one. Only those values are updated that have default values + in the called object and that have non-default values in the given + one. + @return + Returns a list of event types for which event notifications have + to be sent to listeners. The list may be empty or NULL. + */ + ::std::unique_ptr<std::vector<MasterPageContainerChangeEvent::EventType> > + Update ( + const MasterPageDescriptor& rDescriptor); + + /** This convenience method returns either a small or a large preview, + depending on the given size specifier. + Note that the previews are not created when they are not present. + @return + The returned preview may be empty. + */ + const Image& GetPreview (MasterPageContainer::PreviewSize ePreviewSize) const; + + /** Use the PreviewProvider to get access to a preview of the master + page. + + Note that this is only done, when either bForce is <TRUE/> or + the PreviewProvider::GetCostIndex() returns 0. + + The small preview is created by scaling the large one, not by + calling PreviewProvider::operator() a second time. + + It is the responsibility of the caller to call UpdatePageObject() + before calling this method when the PreviewProvider can only work + when the master page object is present, i.e. its NeedsPageObject() + method returns <TRUE/>. + + @param nCostThreshold + When this is zero or positive then the preview is created only + when the preview provider has a cost equal to or smaller than + this threshold. A negative value forces the preview to be + created, regardless of the cost. + @param rSmallSize + Size of the small preview. + @param rLargeSize + Size of the large preview. + @param rRenderer + A PreviewRenderer object that may be used to create a preview. + @return + When the previews are successfully provided then <TRUE/> is + returned. + */ + bool UpdatePreview ( + sal_Int32 nCostThreshold, + const Size& rSmallSize, + const Size& rLargeSize, + ::sd::PreviewRenderer& rRenderer); + + /** Use the PageObjectProvider to get access to the master page object. + + Note that this is only done, when either bForce is <TRUE/> or the + PreviewProvider::GetCostIndex() returns 0. + + @param nCostThreshold + When this is zero or positive then the page object is created + only when the page object provider has a cost equal to or + smaller than this threshold. A negative value forces the + page object be created, regardless of the cost. + @param pDocument + This document of the MasterPageContainer may be used to create + a page object with or store one in. + @return + When the master page object is successfully provided then + 1 is returned, on no change then a 0 is provided, + on a masterpage-error a -1 is provided. + */ + int UpdatePageObject ( + sal_Int32 nCostThreshold, + SdDrawDocument* pDocument); + + enum URLClassification { + URLCLASS_USER, + URLCLASS_LAYOUT, + URLCLASS_PRESENTATION, + URLCLASS_OTHER, + URLCLASS_UNKNOWN, + URLCLASS_UNDETERMINED + }; + + URLClassification GetURLClassification(); + + /** The Token under which the MasterPageContainer gives access to the + object. + */ + MasterPageContainer::Token maToken; + + /** A rough specification of the origin of the master page. + */ + MasterPageContainer::Origin meOrigin; + + /** The URL is not empty for master pages loaded from a template + document. + */ + OUString msURL; + + /** Taken from the title of the template file. + */ + OUString msPageName; + + /** Taken from the master page object. + */ + OUString msStyleName; + + const bool mbIsPrecious; + + /** The actual master page. + */ + SdPage* mpMasterPage; + + /** A slide that uses the master page. + */ + SdPage* mpSlide; + + /** A small (the default size) preview of the master page. May be + empty. When this smaller preview is not empty then the larger one + is not empty, too. + */ + Image maSmallPreview; + + /** A large preview of the master page. May be empty. When this larger + preview is not empty then the smaller one is not empty, too. + */ + Image maLargePreview; + + /** The preview provider. May be empty. May be replaced during the + lifetime of a MasterPageDescriptor object. + */ + std::shared_ptr<PreviewProvider> mpPreviewProvider; + + /** The master page provider. May be empty. May be replaced during + the lifetime of a MasterPageDescriptor object. + */ + std::shared_ptr<PageObjectProvider> mpPageObjectProvider; + + /** This index represents the order in which templates are provided via + the TemplateScanner. It defines the order in which the entries in + the AllMasterPagesSelector are displayed. The default value is -1. + */ + sal_Int32 mnTemplateIndex; + + URLClassification meURLClassification; + + sal_Int32 mnUseCount; + + class URLComparator { public: + OUString msURL; + explicit URLComparator (const OUString& sURL); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class StyleNameComparator { public: + OUString msStyleName; + explicit StyleNameComparator (const OUString& sStyleName); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class PageObjectComparator { public: + const SdPage* mpMasterPage; + explicit PageObjectComparator (const SdPage* pPageObject); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class AllComparator { public: + explicit AllComparator(const SharedMasterPageDescriptor& rDescriptor); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + private: + SharedMasterPageDescriptor mpDescriptor; + }; + +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageObserver.cxx b/sd/source/ui/sidebar/MasterPageObserver.cxx new file mode 100644 index 000000000..017a0bcdf --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageObserver.cxx @@ -0,0 +1,317 @@ +/* -*- 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 <MasterPageObserver.hxx> + +#include <algorithm> +#include <iterator> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <set> +#include <unordered_map> +#include <memory> +#include <vector> +#include <svl/lstner.hxx> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> +#include <tools/debug.hxx> + +namespace sd { + +class MasterPageObserver::Implementation + : public SfxListener +{ +public: + /** The single instance of this class. It is created on demand when + Instance() is called for the first time. + */ + static MasterPageObserver* mpInstance; + + /** The master page observer will listen to events of this document and + detect changes of the use of master pages. + */ + void RegisterDocument (SdDrawDocument& rDocument); + + /** The master page observer will stop to listen to events of this + document. + */ + void UnregisterDocument (SdDrawDocument& rDocument); + + /** Add a listener that is informed of master pages that are newly + assigned to slides or become unassigned. + @param rEventListener + The event listener to call for future events. Call + RemoveEventListener() before the listener is destroyed. + */ + void AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener); + + /** Remove the given listener from the list of listeners. + @param rEventListener + After this method returns the given listener is not called back + from this object. Passing a listener that has not + been registered before is safe and is silently ignored. + */ + void RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener); + +private: + ::std::vector<Link<MasterPageObserverEvent&,void>> maListeners; + + struct DrawDocHash { + size_t operator()(SdDrawDocument* argument) const + { return reinterpret_cast<sal_uIntPtr>(argument); } + }; + typedef std::unordered_map<SdDrawDocument*, + MasterPageObserver::MasterPageNameSet, + DrawDocHash> + MasterPageContainer; + MasterPageContainer maUsedMasterPages; + + virtual void Notify( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + + void AnalyzeUsedMasterPages (SdDrawDocument& rDocument); + + void SendEvent (MasterPageObserverEvent& rEvent); +}; + +MasterPageObserver* MasterPageObserver::Implementation::mpInstance = nullptr; + +//===== MasterPageObserver ==================================================== + +MasterPageObserver& MasterPageObserver::Instance() +{ + if (Implementation::mpInstance == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (Implementation::mpInstance == nullptr) + { + MasterPageObserver* pInstance = new MasterPageObserver (); + SdGlobalResourceContainer::Instance().AddResource ( + ::std::unique_ptr<SdGlobalResource>(pInstance)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + Implementation::mpInstance = pInstance; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + DBG_ASSERT(Implementation::mpInstance!=nullptr, + "MasterPageObserver::Instance(): instance is NULL"); + return *Implementation::mpInstance; +} + +void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument) +{ + mpImpl->RegisterDocument (rDocument); +} + +void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument) +{ + mpImpl->UnregisterDocument (rDocument); +} + +void MasterPageObserver::AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + + mpImpl->AddEventListener (rEventListener); +} + +void MasterPageObserver::RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + mpImpl->RemoveEventListener (rEventListener); +} + +MasterPageObserver::MasterPageObserver() + : mpImpl (new Implementation) +{} + +MasterPageObserver::~MasterPageObserver() +{} + +//===== MasterPageObserver::Implementation ==================================== + +void MasterPageObserver::Implementation::RegisterDocument ( + SdDrawDocument& rDocument) +{ + // Gather the names of all the master pages in the given document. + MasterPageContainer::mapped_type aMasterPageSet; + sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++) + { + SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage != nullptr) + aMasterPageSet.insert (pMasterPage->GetName()); + } + + bool bAlreadyExists = maUsedMasterPages.find(&rDocument) != maUsedMasterPages.end(); + maUsedMasterPages[&rDocument] = aMasterPageSet; + + if (!bAlreadyExists) + StartListening (rDocument); +} + +void MasterPageObserver::Implementation::UnregisterDocument ( + SdDrawDocument& rDocument) +{ + EndListening (rDocument); + + MasterPageContainer::iterator aMasterPageDescriptor(maUsedMasterPages.find(&rDocument)); + if(aMasterPageDescriptor != maUsedMasterPages.end()) + maUsedMasterPages.erase(aMasterPageDescriptor); +} + +void MasterPageObserver::Implementation::AddEventListener ( + const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + if (::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener) != maListeners.end()) + return; + + maListeners.push_back (rEventListener); + + // Tell the new listener about all the master pages that are + // currently in use. + for (const auto& rDocument : maUsedMasterPages) + { + ::std::set<OUString>::reverse_iterator aNameIterator; + for (aNameIterator=rDocument.second.rbegin(); + aNameIterator!=rDocument.second.rend(); + ++aNameIterator) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS, + *aNameIterator); + SendEvent (aEvent); + } + } +} + +void MasterPageObserver::Implementation::RemoveEventListener ( + const Link<MasterPageObserverEvent&,void>& rEventListener) +{ + maListeners.erase ( + ::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener)); +} + +void MasterPageObserver::Implementation::Notify( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + + switch (pSdrHint->GetKind()) + { + case SdrHintKind::PageOrderChange: + // Process the modified set of pages only when the number of + // standard and notes master pages are equal. This test + // filters out events that are sent in between the insertion + // of a new standard master page and a new notes master + // page. + if (auto pDrawDocument = dynamic_cast<SdDrawDocument *>( &rBroadcaster )) + { + if (pDrawDocument->GetMasterSdPageCount(PageKind::Standard) + == pDrawDocument->GetMasterSdPageCount(PageKind::Notes)) + { + AnalyzeUsedMasterPages (*pDrawDocument); + } + } + break; + + default: + break; + } +} + +void MasterPageObserver::Implementation::AnalyzeUsedMasterPages ( + SdDrawDocument& rDocument) +{ + // Create a set of names of the master pages used by the given document. + sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard); + ::std::set<OUString> aCurrentMasterPages; + for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++) + { + SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage != nullptr) + aCurrentMasterPages.insert (pMasterPage->GetName()); + } + + std::vector<OUString> aNewMasterPages; + std::vector<OUString> aRemovedMasterPages; + MasterPageContainer::iterator aOldMasterPagesDescriptor ( + maUsedMasterPages.find(&rDocument)); + if (aOldMasterPagesDescriptor == maUsedMasterPages.end()) + return; + + // Send events about the newly used master pages. + ::std::set_difference ( + aCurrentMasterPages.begin(), + aCurrentMasterPages.end(), + aOldMasterPagesDescriptor->second.begin(), + aOldMasterPagesDescriptor->second.end(), + std::back_inserter(aNewMasterPages)); + for (const auto& aNewMasterPage : aNewMasterPages) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_ADDED, + aNewMasterPage); + SendEvent (aEvent); + } + + // Send events about master pages that are not used any longer. + ::std::set_difference ( + aOldMasterPagesDescriptor->second.begin(), + aOldMasterPagesDescriptor->second.end(), + aCurrentMasterPages.begin(), + aCurrentMasterPages.end(), + std::back_inserter(aRemovedMasterPages)); + for (const auto& aRemovedMasterPage : aRemovedMasterPages) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED, + aRemovedMasterPage); + SendEvent (aEvent); + } + + // Store the new list of master pages. + aOldMasterPagesDescriptor->second = aCurrentMasterPages; +} + +void MasterPageObserver::Implementation::SendEvent ( + MasterPageObserverEvent& rEvent) +{ + for (const auto& aLink : maListeners) + { + aLink.Call(rEvent); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPagesSelector.cxx b/sd/source/ui/sidebar/MasterPagesSelector.cxx new file mode 100644 index 000000000..979726910 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPagesSelector.cxx @@ -0,0 +1,620 @@ +/* -*- 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 <sal/config.h> + +#include <utility> + +#include "MasterPagesSelector.hxx" + +#include "MasterPageContainer.hxx" +#include "DocumentHelper.hxx" +#include <pres.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <app.hrc> + +#include <DrawController.hxx> +#include <SlideSorterViewShell.hxx> +#include "PreviewValueSet.hxx" +#include <ViewShellBase.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/image.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/sidebar/Theme.hxx> +#include <memory> + +using namespace ::com::sun::star::text; + +namespace sd::sidebar { + + /** menu entry that is executed as default action when the left mouse button is + clicked over a master page. + */ +constexpr OStringLiteral gsDefaultClickAction = "applyselect"; + +MasterPagesSelector::MasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar, + const OUString& rUIFileName, + const OString& rValueSetName) + : PanelLayout( pParent, "MasterPagePanel", rUIFileName ), + mpContainer(rpContainer), + mxPreviewValueSet(new PreviewValueSet), + mxPreviewValueSetWin(new weld::CustomWeld(*m_xBuilder, rValueSetName, *mxPreviewValueSet)), + mrDocument(rDocument), + mrBase(rBase), + mxSidebar(rxSidebar) +{ + mxPreviewValueSet->SetSelectHdl ( + LINK(this, MasterPagesSelector, ClickHandler)); + mxPreviewValueSet->SetContextMenuHandler ( + LINK(this, MasterPagesSelector, ContextMenuHandler)); + mxPreviewValueSet->SetStyle(mxPreviewValueSet->GetStyle() | WB_NO_DIRECTSELECT); + + if (mxPreviewValueSet->GetDrawingArea()->get_ref_device().GetDPIScaleFactor() > 1) + mpContainer->SetPreviewSize(MasterPageContainer::LARGE); + + mxPreviewValueSet->SetPreviewSize(mpContainer->GetPreviewSizePixel()); + mxPreviewValueSet->Show(); + + mxPreviewValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); + + Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener)); + mpContainer->AddChangeListener(aChangeListener); +} + +MasterPagesSelector::~MasterPagesSelector() +{ + Clear(); + UpdateLocks(ItemList()); + + Link<MasterPageContainerChangeEvent&,void> aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener)); + mpContainer->RemoveChangeListener(aChangeListener); + mpContainer.reset(); + mxPreviewValueSetWin.reset(); + mxPreviewValueSet.reset(); +} + +void MasterPagesSelector::LateInit() +{ +} + +sal_Int32 MasterPagesSelector::GetPreferredHeight (sal_Int32 nWidth) +{ + const ::osl::MutexGuard aGuard (maMutex); + + return mxPreviewValueSet->GetPreferredHeight (nWidth); +} + +void MasterPagesSelector::UpdateLocks (const ItemList& rItemList) +{ + ItemList aNewLockList; + + // In here we first lock the master pages in the given list and then + // release the locks acquired in a previous call to this method. When + // this were done the other way round the lock count of some master + // pages might drop temporarily to 0 and would lead to unnecessary + // deletion and re-creation of MasterPageDescriptor objects. + + // Lock the master pages in the given list. + for (const auto& rItem : rItemList) + { + mpContainer->AcquireToken(rItem); + aNewLockList.push_back(rItem); + } + + // Release the previously locked master pages. + for (const auto& rPage : maLockedMasterPages) + mpContainer->ReleaseToken(rPage); + + maLockedMasterPages.swap(aNewLockList); +} + +void MasterPagesSelector::Fill() +{ + ::std::unique_ptr<ItemList> pItemList (new ItemList); + + Fill(*pItemList); + + UpdateLocks(*pItemList); + UpdateItemList(std::move(pItemList)); +} + +OUString MasterPagesSelector::GetContextMenuUIFile() const +{ + return "modules/simpress/ui/mastermenu.ui"; +} + +IMPL_LINK_NOARG(MasterPagesSelector, ClickHandler, ValueSet*, void) +{ + // We use the framework to assign the clicked-on master page because we + // so use the same mechanism as the context menu does (where we do not + // have the option to call the assignment method directly.) + ExecuteCommand(gsDefaultClickAction); +} + +IMPL_LINK(MasterPagesSelector, ContextMenuHandler, const Point*, pPos, void) +{ + if (pPos) + { + // Here we only prepare the display of the context menu: on right + // click the item under the mouse is selected. + mxPreviewValueSet->GrabFocus(); + mxPreviewValueSet->ReleaseMouse(); + + sal_uInt16 nIndex = mxPreviewValueSet->GetItemId(*pPos); + if (nIndex > 0) + mxPreviewValueSet->SelectItem(nIndex); + } + + // Now do the actual display of the context menu + ShowContextMenu(pPos); +} + +void MasterPagesSelector::ShowContextMenu(const Point* pPos) +{ + // Use the currently selected item and show the popup menu in its + // center. + const sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + if (nIndex <= 0) + return; + + // The position of the upper left corner of the context menu is + // taken either from the mouse position (when the command was sent + // as reaction to a right click) or in the center of the selected + // item (when the command was sent as reaction to Shift+F10.) + Point aPosition; + if (!pPos) + { + ::tools::Rectangle aBBox (mxPreviewValueSet->GetItemRect(nIndex)); + aPosition = aBBox.Center(); + } + else + aPosition = *pPos; + + // Setup the menu. + weld::Widget* pParent = mxPreviewValueSet->GetDrawingArea(); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pParent, GetContextMenuUIFile())); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + ProcessPopupMenu(*xMenu); + ::tools::Rectangle aRect(aPosition, Size(1,1)); + // Show the menu. + ExecuteCommand(xMenu->popup_at_rect(pParent, aRect)); +} + +void MasterPagesSelector::ProcessPopupMenu(weld::Menu& rMenu) +{ + // Disable some entries. + if (mpContainer->GetPreviewSize() == MasterPageContainer::SMALL) + rMenu.set_sensitive("small", false); + else + rMenu.set_sensitive("large", false); +} + +void MasterPagesSelector::ExecuteCommand(const OString &rIdent) +{ + if (rIdent == "applyall") + { + mrBase.SetBusyState (true); + AssignMasterPageToAllSlides (GetSelectedMasterPage()); + mrBase.SetBusyState (false); + } + else if (rIdent == "applyselect") + { + mrBase.SetBusyState (true); + AssignMasterPageToSelectedSlides (GetSelectedMasterPage()); + mrBase.SetBusyState (false); + } + else if (rIdent == "large") + { + mrBase.SetBusyState (true); + mpContainer->SetPreviewSize(MasterPageContainer::LARGE); + mrBase.SetBusyState (false); + if (mxSidebar.is()) + mxSidebar->requestLayout(); + } + else if (rIdent == "small") + { + mrBase.SetBusyState (true); + mpContainer->SetPreviewSize(MasterPageContainer::SMALL); + mrBase.SetBusyState (false); + if (mxSidebar.is()) + mxSidebar->requestLayout(); + } + else if (rIdent == "edit") + { + using namespace ::com::sun::star; + uno::Reference<drawing::XDrawPage> xSelectedMaster; + SdPage* pMasterPage = GetSelectedMasterPage(); + assert(pMasterPage); //rhbz#902884 + if (pMasterPage) + xSelectedMaster.set(pMasterPage->getUnoPage(), uno::UNO_QUERY); + SfxViewFrame* pViewFrame = mrBase.GetViewFrame(); + if (pViewFrame != nullptr && xSelectedMaster.is()) + { + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if (pDispatcher != nullptr) + { + sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + pDispatcher->Execute(SID_MASTERPAGE, SfxCallMode::SYNCHRON); + mxPreviewValueSet->SelectItem (nIndex); + mrBase.GetDrawController().setCurrentPage(xSelectedMaster); + } + } + } +} + +IMPL_LINK(MasterPagesSelector, ContainerChangeListener, MasterPageContainerChangeEvent&, rEvent, void) +{ + NotifyContainerChangeEvent(rEvent); +} + +SdPage* MasterPagesSelector::GetSelectedMasterPage() +{ + const ::osl::MutexGuard aGuard (maMutex); + + SdPage* pMasterPage = nullptr; + sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + pMasterPage = mpContainer->GetPageObjectForToken(pData->second, true); + } + return pMasterPage; +} + +/** Assemble a list of all slides of the document and pass it to + AssignMasterPageToPageList(). +*/ +void MasterPagesSelector::AssignMasterPageToAllSlides (SdPage* pMasterPage) +{ + if (pMasterPage == nullptr) + return; + + sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard); + if (nPageCount == 0) + return; + + // Get a list of all pages. As a little optimization we only + // include pages that do not already have the given master page + // assigned. + OUString sFullLayoutName(pMasterPage->GetLayoutName()); + ::sd::slidesorter::SharedPageSelection pPageList = + std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + for (sal_uInt16 nPageIndex=0; nPageIndex<nPageCount; nPageIndex++) + { + SdPage* pPage = mrDocument.GetSdPage (nPageIndex, PageKind::Standard); + if (pPage != nullptr && pPage->GetLayoutName() != sFullLayoutName) + { + pPageList->push_back (pPage); + } + } + + AssignMasterPageToPageList(pMasterPage, pPageList); +} + +/** Assemble a list of the currently selected slides (selected in a visible + slide sorter) and pass it to AssignMasterPageToPageList(). +*/ +void MasterPagesSelector::AssignMasterPageToSelectedSlides ( + SdPage* pMasterPage) +{ + using namespace ::sd::slidesorter; + using namespace ::sd::slidesorter::controller; + + if (pMasterPage == nullptr) + return; + + // Find a visible slide sorter. + SlideSorterViewShell* pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase); + if (pSlideSorter == nullptr) + return; + + // Get a list of selected pages. + SharedPageSelection pPageSelection = pSlideSorter->GetPageSelection(); + if (pPageSelection->empty()) + return; + + AssignMasterPageToPageList(pMasterPage, pPageSelection); + + // Restore the previous selection. + pSlideSorter->SetPageSelection(pPageSelection); +} + +void MasterPagesSelector::AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*>>& rPageList) +{ + DocumentHelper::AssignMasterPageToPageList(mrDocument, pMasterPage, rPageList); +} + +void MasterPagesSelector::NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent) +{ + const ::osl::MutexGuard aGuard (maMutex); + + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::SIZE_CHANGED: + mxPreviewValueSet->SetPreviewSize(mpContainer->GetPreviewSizePixel()); + UpdateAllPreviews(); + break; + + case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED: + { + int nIndex (GetIndexForToken(rEvent.maChildToken)); + if (nIndex >= 0) + { + mxPreviewValueSet->SetItemImage ( + static_cast<sal_uInt16>(nIndex), + mpContainer->GetPreviewForToken(rEvent.maChildToken)); + mxPreviewValueSet->Invalidate(mxPreviewValueSet->GetItemRect(static_cast<sal_uInt16>(nIndex))); + } + } + break; + + case MasterPageContainerChangeEvent::EventType::DATA_CHANGED: + { + InvalidateItem(rEvent.maChildToken); + Fill(); + } + break; + + case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED: + { + int nIndex (GetIndexForToken(rEvent.maChildToken)); + SetItem(nIndex, MasterPageContainer::NIL_TOKEN); + break; + } + + default: + break; + } +} + +MasterPagesSelector::UserData* MasterPagesSelector::GetUserData (int nIndex) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + if (nIndex>0 && o3tl::make_unsigned(nIndex)<=mxPreviewValueSet->GetItemCount()) + return static_cast<UserData*>(mxPreviewValueSet->GetItemData(static_cast<sal_uInt16>(nIndex))); + else + return nullptr; +} + +void MasterPagesSelector::SetUserData (int nIndex, std::unique_ptr<UserData> pData) +{ + const ::osl::MutexGuard aGuard (maMutex); + + delete GetUserData(nIndex); + mxPreviewValueSet->SetItemData(static_cast<sal_uInt16>(nIndex), pData.release()); +} + +void MasterPagesSelector::SetItem ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + RemoveTokenToIndexEntry(nIndex,aToken); + + if (nIndex <= 0) + return; + + if (aToken != MasterPageContainer::NIL_TOKEN) + { + Image aPreview (mpContainer->GetPreviewForToken(aToken)); + MasterPageContainer::PreviewState eState (mpContainer->GetPreviewState(aToken)); + + if (aPreview.GetSizePixel().Width()>0) + { + if (mxPreviewValueSet->GetItemPos(nIndex) != VALUESET_ITEM_NOTFOUND) + { + mxPreviewValueSet->SetItemImage(nIndex,aPreview); + mxPreviewValueSet->SetItemText(nIndex, mpContainer->GetPageNameForToken(aToken)); + } + else + { + mxPreviewValueSet->InsertItem ( + nIndex, + aPreview, + mpContainer->GetPageNameForToken(aToken), + nIndex); + } + SetUserData(nIndex, std::make_unique<UserData>(nIndex,aToken)); + + AddTokenToIndexEntry(nIndex,aToken); + } + + if (eState == MasterPageContainer::PS_CREATABLE) + mpContainer->RequestPreview(aToken); + } + else + { + mxPreviewValueSet->RemoveItem(nIndex); + } + +} + +void MasterPagesSelector::AddTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + maTokenToValueSetIndex[aToken] = nIndex; +} + +void MasterPagesSelector::RemoveTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aNewToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + // Get the token that the index pointed to previously. + MasterPageContainer::Token aOldToken (pData->second); + + if (aNewToken != aOldToken + && nIndex == GetIndexForToken(aOldToken)) + { + maTokenToValueSetIndex[aOldToken] = 0; + } + } +} + +void MasterPagesSelector::InvalidatePreview (const SdPage* pPage) +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + MasterPageContainer::Token aToken (pData->second); + if (pPage == mpContainer->GetPageObjectForToken(aToken,false)) + { + mpContainer->InvalidatePreview(aToken); + mpContainer->RequestPreview(aToken); + break; + } + } + } +} + +void MasterPagesSelector::UpdateAllPreviews() +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + MasterPageContainer::Token aToken (pData->second); + mxPreviewValueSet->SetItemImage( + nIndex, + mpContainer->GetPreviewForToken(aToken)); + if (mpContainer->GetPreviewState(aToken) == MasterPageContainer::PS_CREATABLE) + mpContainer->RequestPreview(aToken); + } + } + mxPreviewValueSet->Rearrange(); +} + +void MasterPagesSelector::ClearPageSet() +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + delete pData; + } + mxPreviewValueSet->Clear(); +} + +void MasterPagesSelector::SetHelpId( const OString& aId ) +{ + const ::osl::MutexGuard aGuard (maMutex); + + mxPreviewValueSet->SetHelpId( aId ); +} + +sal_Int32 MasterPagesSelector::GetIndexForToken (MasterPageContainer::Token aToken) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + TokenToValueSetIndex::const_iterator iIndex (maTokenToValueSetIndex.find(aToken)); + if (iIndex != maTokenToValueSetIndex.end()) + return iIndex->second; + else + return -1; +} + +void MasterPagesSelector::Clear() +{ + const ::osl::MutexGuard aGuard (maMutex); + + ClearPageSet(); +} + +void MasterPagesSelector::InvalidateItem (MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + auto iItem = std::find(maCurrentItemList.begin(), maCurrentItemList.end(), aToken); + if (iItem != maCurrentItemList.end()) + *iItem = MasterPageContainer::NIL_TOKEN; +} + +void MasterPagesSelector::UpdateItemList (::std::unique_ptr<ItemList> && pNewItemList) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ItemList::const_iterator iNewItem (pNewItemList->begin()); + ItemList::const_iterator iCurrentItem (maCurrentItemList.begin()); + ItemList::const_iterator iNewEnd (pNewItemList->end()); + ItemList::const_iterator iCurrentEnd (maCurrentItemList.end()); + sal_uInt16 nIndex (1); + + // Update existing items. + for ( ; iNewItem!=iNewEnd && iCurrentItem!=iCurrentEnd; ++iNewItem, ++iCurrentItem,++nIndex) + { + if (*iNewItem != *iCurrentItem) + { + SetItem(nIndex,*iNewItem); + } + } + + // Append new items. + for ( ; iNewItem!=iNewEnd; ++iNewItem,++nIndex) + { + SetItem(nIndex,*iNewItem); + } + + // Remove trailing items. + for ( ; iCurrentItem!=iCurrentEnd; ++iCurrentItem,++nIndex) + { + SetItem(nIndex,MasterPageContainer::NIL_TOKEN); + } + + maCurrentItemList.swap(*pNewItemList); + + mxPreviewValueSet->Rearrange(); + if (mxSidebar.is()) + mxSidebar->requestLayout(); +} + +css::ui::LayoutSize MasterPagesSelector::GetHeightForWidth (const sal_Int32 nWidth) +{ + const sal_Int32 nHeight (GetPreferredHeight(nWidth)); + return css::ui::LayoutSize(nHeight,nHeight,nHeight); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPagesSelector.hxx b/sd/source/ui/sidebar/MasterPagesSelector.hxx new file mode 100644 index 000000000..1b6932789 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPagesSelector.hxx @@ -0,0 +1,180 @@ +/* -*- 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 . + */ + +#pragma once + +#include <map> +#include <memory> +#include "MasterPageContainer.hxx" +#include "PreviewValueSet.hxx" +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <sfx2/sidebar/PanelLayout.hxx> + +#include <osl/mutex.hxx> + +namespace com::sun::star::ui { class XSidebar; } +class MouseEvent; +class SdDrawDocument; +class SdPage; + +namespace sd { +class ViewShellBase; +} + +namespace sd::sidebar { + +/** Base class of a menu that lets the user select from a list of + templates or designs that are loaded from files. +*/ +class MasterPagesSelector : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +public: + MasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar, + const OUString& rUIFileName, + const OString& rValueSetName); + virtual ~MasterPagesSelector() override; + + virtual void LateInit(); + + sal_Int32 GetPreferredHeight (sal_Int32 nWidth); + + /** Make the selector empty. This method clear the value set from any + entries. Override this method to add functionality, especially to + destroy objects set as data items at the value set. + */ + void ClearPageSet(); + + void SetHelpId( const OString& aId ); + + /** Mark the preview that belongs to the given index as not up-to-date + anymore with respect to page content or preview size. + The implementation of this method will either sunchronously or + asynchronously call UpdatePreview(). + @param nIndex + Index into the value set control that is used for displaying the + previews. + */ + void InvalidatePreview (const SdPage* pPage); + + void UpdateAllPreviews(); + + void ShowContextMenu(const Point* pPos); + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + +protected: + mutable ::osl::Mutex maMutex; + std::shared_ptr<MasterPageContainer> mpContainer; + + std::unique_ptr<PreviewValueSet> mxPreviewValueSet; + std::unique_ptr<weld::CustomWeld> mxPreviewValueSetWin; + + SdDrawDocument& mrDocument; + ViewShellBase& mrBase; + + SdPage* GetSelectedMasterPage(); + + /** Assign the given master page to all slides of the document. + @param pMasterPage + The master page to assign to all slides. + */ + void AssignMasterPageToAllSlides (SdPage* pMasterPage); + + /** Assign the given master page to all slides that are selected in a + slide sorter that is displayed in the lef or center pane. When both + panes display a slide sorter then the one in the center pane is + used. + */ + void AssignMasterPageToSelectedSlides (SdPage* pMasterPage); + + virtual void AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*>>& rPageList); + + virtual void NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent); + + typedef ::std::pair<int, MasterPageContainer::Token> UserData; + UserData* GetUserData (int nIndex) const; + void SetUserData (int nIndex, std::unique_ptr<UserData> pData); + + sal_Int32 GetIndexForToken (MasterPageContainer::Token aToken) const; + typedef ::std::vector<MasterPageContainer::Token> ItemList; + void UpdateItemList (::std::unique_ptr<ItemList> && pList); + void Clear(); + /** Invalidate the specified item so that on the next Fill() this item + is updated. + */ + void InvalidateItem (MasterPageContainer::Token aToken); + + // For every item in the ValueSet we store its associated token. This + // allows a faster access and easier change tracking. + ItemList maCurrentItemList; + typedef ::std::map<MasterPageContainer::Token,sal_Int32> TokenToValueSetIndex; + TokenToValueSetIndex maTokenToValueSetIndex; + + ItemList maLockedMasterPages; + /** Lock master pages in the given list and release locks that were + previously acquired. + */ + void UpdateLocks (const ItemList& rItemList); + + void Fill(); + virtual void Fill (ItemList& rItemList) = 0; + + /** Give derived classes the opportunity to provide their own context + menu. If they do then they probably have to provide their own + Execute() and GetState() methods as well. + */ + virtual OUString GetContextMenuUIFile() const; + + virtual void ProcessPopupMenu(weld::Menu& rMenu); + virtual void ExecuteCommand(const OString& rIdent); + +private: + css::uno::Reference<css::ui::XSidebar> mxSidebar; + + /** The offset between ValueSet index and MasterPageContainer::Token + last seen. This value is used heuristically to speed up the lookup + of an index for a token. + */ + DECL_LINK(ClickHandler, ValueSet*, void); + DECL_LINK(ContextMenuHandler, const Point*, void); + DECL_LINK(ContainerChangeListener, MasterPageContainerChangeEvent&, void); + + void SetItem ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); + void AddTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); + void RemoveTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/NavigatorWrapper.cxx b/sd/source/ui/sidebar/NavigatorWrapper.cxx new file mode 100644 index 000000000..95d4a66ae --- /dev/null +++ b/sd/source/ui/sidebar/NavigatorWrapper.cxx @@ -0,0 +1,49 @@ +/* -*- 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 "NavigatorWrapper.hxx" +#include <ViewShellBase.hxx> +#include <navigatr.hxx> + +namespace sd::sidebar { + +NavigatorWrapper::NavigatorWrapper ( + weld::Widget* pParent, + sd::ViewShellBase& rViewShellBase, + SfxBindings* pBindings) + : SdNavigatorWin(pParent, pBindings, nullptr) + , mrViewShellBase(rViewShellBase) +{ + SetUpdateRequestFunctor( + [this] () { return this->UpdateNavigator(); }); +} + +css::ui::LayoutSize NavigatorWrapper::GetHeightForWidth (const sal_Int32) +{ + return css::ui::LayoutSize(-1,-1,-1); +} + +void NavigatorWrapper::UpdateNavigator() +{ + InitTreeLB(mrViewShellBase.GetDocument()); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/NavigatorWrapper.hxx b/sd/source/ui/sidebar/NavigatorWrapper.hxx new file mode 100644 index 000000000..6632d796f --- /dev/null +++ b/sd/source/ui/sidebar/NavigatorWrapper.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ +#pragma once + +#include <sfx2/sidebar/ILayoutableWindow.hxx> +#include <navigatr.hxx> + +class SfxBindings; +namespace sd { class ViewShellBase; } + +namespace sd::sidebar { + +/** Present the navigator as control that can be displayed inside the + sidebar. + This wrapper has two main responsibilities: + - Watch for document changes and update the navigator when one + happens. + - Forward size changes from sidebar to navigator. +*/ +class NavigatorWrapper + : public SdNavigatorWin, + public sfx2::sidebar::ILayoutableWindow +{ +public: + NavigatorWrapper ( + weld::Widget* pParent, + sd::ViewShellBase& rViewShellBase, + SfxBindings* pBindings); + + // From ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + +private: + ViewShellBase& mrViewShellBase; + + void UpdateNavigator(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PageMarginUtils.hxx b/sd/source/ui/sidebar/PageMarginUtils.hxx new file mode 100644 index 000000000..9a1f83493 --- /dev/null +++ b/sd/source/ui/sidebar/PageMarginUtils.hxx @@ -0,0 +1,159 @@ +/* -*- 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 <cmath> +#include <tools/long.hxx> +#define SDPAGE_NO_MARGIN 0 +#define SDPAGE_NARROW_VALUE 635 +#define SDPAGE_MODERATE_LR 955 +#define SDPAGE_NORMAL_VALUE 1000 +#define SDPAGE_WIDE_VALUE1 1270 +#define SDPAGE_WIDE_VALUE2 2540 +#define SDPAGE_WIDE_VALUE3 1590 +#define SDPAGE_UNIT_THRESHOLD 5 + +namespace sd::sidebar{ + +bool IsNone( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NO_MARGIN ) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNone( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NO_MARGIN; + nPageRightMargin = SDPAGE_NO_MARGIN; + nPageTopMargin = SDPAGE_NO_MARGIN; + nPageBottomMargin = SDPAGE_NO_MARGIN; +} + +bool IsNarrow( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNarrow( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NARROW_VALUE; + nPageRightMargin = SDPAGE_NARROW_VALUE; + nPageTopMargin = SDPAGE_NARROW_VALUE; + nPageBottomMargin = SDPAGE_NARROW_VALUE; +} + +bool IsModerate( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_MODERATE_LR) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_MODERATE_LR) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetModerate( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_MODERATE_LR; + nPageRightMargin = SDPAGE_MODERATE_LR; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsNormal075( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal075( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NORMAL_VALUE; + nPageRightMargin = SDPAGE_NORMAL_VALUE; + nPageTopMargin = SDPAGE_NORMAL_VALUE; + nPageBottomMargin = SDPAGE_NORMAL_VALUE; +} + +bool IsNormal100( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal100( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE1; + nPageRightMargin = SDPAGE_WIDE_VALUE1; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsNormal125( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE3) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE3) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal125( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE3; + nPageRightMargin = SDPAGE_WIDE_VALUE3; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsWide( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE2) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE2) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetWide( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE2; + nPageRightMargin = SDPAGE_WIDE_VALUE2; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PanelFactory.cxx b/sd/source/ui/sidebar/PanelFactory.cxx new file mode 100644 index 000000000..c7ca8c25c --- /dev/null +++ b/sd/source/ui/sidebar/PanelFactory.cxx @@ -0,0 +1,141 @@ +/* -*- 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 "PanelFactory.hxx" +#include <framework/Pane.hxx> +#include <ViewShellBase.hxx> +#include <DrawController.hxx> +#include "LayoutMenu.hxx" +#include "CurrentMasterPagesSelector.hxx" +#include "RecentMasterPagesSelector.hxx" +#include "AllMasterPagesSelector.hxx" +#include <CustomAnimationPane.hxx> +#include "NavigatorWrapper.hxx" +#include <SlideTransitionPane.hxx> +#include <TableDesignPane.hxx> +#include "SlideBackground.hxx" + +#include <sfx2/sidebar/SidebarPanelBase.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/servicehelper.hxx> +#include <vcl/weldutils.hxx> + +using namespace css; +using namespace css::uno; +using namespace ::sd::framework; + +namespace sd::sidebar { + +//----- PanelFactory -------------------------------------------------------- + +PanelFactory::PanelFactory() +{ +} + +PanelFactory::~PanelFactory() +{ +} + +// XUIElementFactory + +Reference<ui::XUIElement> SAL_CALL PanelFactory::createUIElement ( + const OUString& rsUIElementResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& rArguments) +{ + // Process arguments. + const ::comphelper::NamedValueCollection aArguments (rArguments); + Reference<frame::XFrame> xFrame (aArguments.getOrDefault("Frame", Reference<frame::XFrame>())); + Reference<awt::XWindow> xParentWindow (aArguments.getOrDefault("ParentWindow", Reference<awt::XWindow>())); + Reference<ui::XSidebar> xSidebar (aArguments.getOrDefault("Sidebar", Reference<ui::XSidebar>())); + + // Throw exceptions when the arguments are not as expected. + weld::Widget* pParent(nullptr); + if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get())) + pParent = pTunnel->getWidget(); + + if (!pParent) + throw RuntimeException( + "PanelFactory::createUIElement called without ParentWindow"); + if ( ! xFrame.is()) + throw RuntimeException( + "PanelFactory::createUIElement called without XFrame"); + + // Tunnel through the controller to obtain a ViewShellBase. + ViewShellBase* pBase = nullptr; + auto pController = comphelper::getFromUnoTunnel<sd::DrawController>(xFrame->getController()); + if (pController != nullptr) + pBase = pController->GetViewShellBase(); + if (pBase == nullptr) + throw RuntimeException("can not get ViewShellBase for frame"); + + // Get bindings from given arguments. + const sal_uInt64 nBindingsValue (aArguments.getOrDefault("SfxBindings", sal_uInt64(0))); + SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue); + + // Create a framework view. + std::unique_ptr<PanelLayout> xControl; + css::ui::LayoutSize aLayoutSize (-1,-1,-1); + + /** Note that these names have to be identical to (the tail of) + the entries in officecfg/registry/data/org/openoffice/Office/Impress.xcu + for the TaskPanelFactory. + */ + if (rsUIElementResourceURL.endsWith("/CustomAnimations")) + xControl = std::make_unique<CustomAnimationPane>(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/Layouts")) + xControl = std::make_unique<LayoutMenu>(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/AllMasterPages")) + xControl = AllMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/RecentMasterPages")) + xControl = RecentMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/UsedMasterPages")) + xControl = CurrentMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/SlideTransitions")) + xControl = std::make_unique<SlideTransitionPane>(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/TableDesign")) + xControl = std::make_unique<TableDesignPane>(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/NavigatorPanel")) + xControl = std::make_unique<NavigatorWrapper>(pParent, *pBase, pBindings); + else if (rsUIElementResourceURL.endsWith("/SlideBackgroundPanel")) + xControl = std::make_unique<SlideBackground>(pParent, *pBase, xFrame, pBindings); + + if (!xControl) + throw lang::IllegalArgumentException(); + + // Create a wrapper around the control that implements the + // necessary UNO interfaces. + return sfx2::sidebar::SidebarPanelBase::Create( + rsUIElementResourceURL, + xFrame, + std::move(xControl), + aLayoutSize); +} + +} // end of namespace sd::sidebar + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_openoffice_comp_Draw_framework_PanelFactory_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new sd::sidebar::PanelFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PanelFactory.hxx b/sd/source/ui/sidebar/PanelFactory.hxx new file mode 100644 index 000000000..77fc17dbc --- /dev/null +++ b/sd/source/ui/sidebar/PanelFactory.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ +#pragma once + +#include <comphelper/compbase.hxx> + +#include <com/sun/star/ui/XUIElementFactory.hpp> + +namespace sd::sidebar { + +typedef comphelper::WeakComponentImplHelper < + css::ui::XUIElementFactory + > PanelFactoryInterfaceBase; + +class PanelFactory final + : public PanelFactoryInterfaceBase +{ +public: + explicit PanelFactory (); + virtual ~PanelFactory() override; + PanelFactory(const PanelFactory&) = delete; + PanelFactory& operator=(const PanelFactory&) = delete; + + // XUIElementFactory + + css::uno::Reference<css::ui::XUIElement> SAL_CALL createUIElement ( + const OUString& rsResourceURL, + const css::uno::Sequence<css::beans::PropertyValue>& rArguments) override; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PreviewValueSet.cxx b/sd/source/ui/sidebar/PreviewValueSet.cxx new file mode 100644 index 000000000..f752d60eb --- /dev/null +++ b/sd/source/ui/sidebar/PreviewValueSet.cxx @@ -0,0 +1,127 @@ +/* -*- 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 "PreviewValueSet.hxx" +#include <vcl/commandevent.hxx> + +namespace sd::sidebar { + +const int gnBorderWidth(3); +const int gnBorderHeight(3); + +PreviewValueSet::PreviewValueSet() + : ValueSet(nullptr) + , maPreviewSize(10,10) +{ + SetStyle ( + GetStyle() + & ~(WB_ITEMBORDER)// | WB_MENUSTYLEVALUESET) + // | WB_FLATVALUESET); + ); +} + +void PreviewValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + ValueSet::SetDrawingArea(pDrawingArea); + + SetColCount(2); + SetExtraSpacing (2); +} + +PreviewValueSet::~PreviewValueSet() +{ +} + +void PreviewValueSet::SetPreviewSize (const Size& rSize) +{ + maPreviewSize = rSize; +} + +void PreviewValueSet::SetContextMenuHandler(const Link<const Point*, void>& rLink) +{ + maContextMenuHandler = rLink; +} + +bool PreviewValueSet::Command(const CommandEvent& rEvent) +{ + if (rEvent.GetCommand() != CommandEventId::ContextMenu) + return ValueSet::Command(rEvent); + maContextMenuHandler.Call(rEvent.IsMouseEvent() ? &rEvent.GetMousePosPixel() : nullptr); + return true; +} + +void PreviewValueSet::Resize() +{ + ValueSet::Resize(); + + Size aWindowSize (GetOutputSizePixel()); + if (!aWindowSize.IsEmpty()) + { + Rearrange(); + } +} + +void PreviewValueSet::Rearrange() +{ + sal_uInt16 nNewColumnCount (CalculateColumnCount ( + GetOutputSizePixel().Width())); + sal_uInt16 nNewRowCount (CalculateRowCount (nNewColumnCount)); + + SetFormat(); + SetColCount(nNewColumnCount); + SetLineCount(nNewRowCount); +} + +sal_uInt16 PreviewValueSet::CalculateColumnCount (int nWidth) const +{ + int nColumnCount = 0; + if (nWidth > 0) + { + nColumnCount = nWidth / (maPreviewSize.Width() + 2*gnBorderWidth); + if (nColumnCount < 1) + nColumnCount = 1; + } + return static_cast<sal_uInt16>(nColumnCount); +} + +sal_uInt16 PreviewValueSet::CalculateRowCount (sal_uInt16 nColumnCount) const +{ + int nRowCount = 0; + int nItemCount = GetItemCount(); + if (nColumnCount > 0) + { + nRowCount = (nItemCount+nColumnCount-1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + } + + return static_cast<sal_uInt16>(nRowCount); +} + +sal_Int32 PreviewValueSet::GetPreferredHeight (sal_Int32 nWidth) +{ + int nRowCount (CalculateRowCount(CalculateColumnCount(nWidth))); + int nItemHeight (maPreviewSize.Height()); + + return nRowCount * (nItemHeight + 2*gnBorderHeight); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PreviewValueSet.hxx b/sd/source/ui/sidebar/PreviewValueSet.hxx new file mode 100644 index 000000000..adab3c78a --- /dev/null +++ b/sd/source/ui/sidebar/PreviewValueSet.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svtools/valueset.hxx> + +namespace sd::sidebar +{ +/** Adapt the svtools valueset to the needs of the master page controls. +*/ +class PreviewValueSet : public ValueSet +{ +public: + explicit PreviewValueSet(); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual ~PreviewValueSet() override; + + void SetContextMenuHandler(const Link<const Point*, void>& rLink); + + virtual void Resize() override; + virtual bool Command(const CommandEvent& rEvent) override; + + void SetPreviewSize(const Size& rSize); + + sal_Int32 GetPreferredHeight(sal_Int32 nWidth); + + /** Set the number of rows and columns according to the current number + of items. Call this method when new items have been inserted. + */ + void Rearrange(); + +private: + Link<const Point*, void> maContextMenuHandler; + Size maPreviewSize; + + sal_uInt16 CalculateColumnCount(int nWidth) const; + sal_uInt16 CalculateRowCount(sal_uInt16 nColumnCount) const; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx b/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx new file mode 100644 index 000000000..6e5a46c73 --- /dev/null +++ b/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx @@ -0,0 +1,138 @@ +/* -*- 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 "RecentMasterPagesSelector.hxx" + +#include <ViewShellBase.hxx> +#include "RecentlyUsedMasterPages.hxx" +#include <MasterPageObserver.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <helpids.h> + +namespace sd::sidebar { + +std::unique_ptr<PanelLayout> RecentMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared<MasterPageContainer>(); + + auto xSelector(std::make_unique<RecentMasterPagesSelector>( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId(HID_SD_TASK_PANE_PREVIEW_RECENT); + + return xSelector; +} + +RecentMasterPagesSelector::RecentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar) + : MasterPagesSelector (pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanelrecent.ui", "recentvalueset") +{ +} + +RecentMasterPagesSelector::~RecentMasterPagesSelector() +{ + RecentlyUsedMasterPages::Instance().RemoveEventListener ( + LINK(this,RecentMasterPagesSelector,MasterPageListListener)); +} + +void RecentMasterPagesSelector::LateInit() +{ + MasterPagesSelector::LateInit(); + + MasterPagesSelector::Fill(); + RecentlyUsedMasterPages::Instance().AddEventListener ( + LINK(this,RecentMasterPagesSelector,MasterPageListListener)); +} + +IMPL_LINK_NOARG(RecentMasterPagesSelector, MasterPageListListener, LinkParamNone*, void) +{ + MasterPagesSelector::Fill(); +} + +void RecentMasterPagesSelector::Fill (ItemList& rItemList) +{ + // Create a set of names of the master pages used by the document. + MasterPageObserver::MasterPageNameSet aCurrentNames; + sal_uInt16 nMasterPageCount = mrDocument.GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++) + { + SdPage* pMasterPage = mrDocument.GetMasterSdPage (nIndex, PageKind::Standard); + if (pMasterPage != nullptr) + aCurrentNames.insert (pMasterPage->GetName()); + } + + // Insert the recently used master pages that are currently not used. + RecentlyUsedMasterPages& rInstance (RecentlyUsedMasterPages::Instance()); + int nPageCount = rInstance.GetMasterPageCount(); + for (int nIndex=0; nIndex<nPageCount; nIndex++) + { + // Add an entry when a) the page is already known to the + // MasterPageContainer, b) the style name is empty, i.e. it has not yet + // been loaded (and thus can not be in use) or otherwise c) the + // style name is not currently in use. + MasterPageContainer::Token aToken (rInstance.GetTokenForIndex(nIndex)); + if (aToken != MasterPageContainer::NIL_TOKEN) + { + OUString sStyleName (mpContainer->GetStyleNameForToken(aToken)); + if (sStyleName.isEmpty() + || aCurrentNames.find(sStyleName) == aCurrentNames.end()) + { + rItemList.push_back(aToken); + } + } + } +} + +void RecentMasterPagesSelector::AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) +{ + sal_uInt16 nSelectedItemId = mxPreviewValueSet->GetSelectedItemId(); + + MasterPagesSelector::AssignMasterPageToPageList(pMasterPage, rpPageList); + + // Restore the selection. + if (mxPreviewValueSet->GetItemCount() > 0) + { + if (mxPreviewValueSet->GetItemCount() >= nSelectedItemId) + mxPreviewValueSet->SelectItem(nSelectedItemId); + else + mxPreviewValueSet->SelectItem(mxPreviewValueSet->GetItemCount()); + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx b/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx new file mode 100644 index 000000000..6dbc3a2aa --- /dev/null +++ b/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx @@ -0,0 +1,71 @@ +/* -*- 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 . + */ + +#pragma once + +#include "MasterPagesSelector.hxx" + +namespace sd::sidebar { + +/** Show the recently used master pages (that are not currently used). +*/ +class RecentMasterPagesSelector final + : public MasterPagesSelector +{ + friend class VclPtrInstance<RecentMasterPagesSelector>; +public: + static std::unique_ptr<PanelLayout> Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + + RecentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr<MasterPageContainer>& rpContainer, + const css::uno::Reference<css::ui::XSidebar>& rxSidebar); + virtual ~RecentMasterPagesSelector() override; + +private: + DECL_LINK(MasterPageListListener, LinkParamNone*, void); + virtual void Fill (ItemList& rItemList) override; + + using sd::sidebar::MasterPagesSelector::Fill; + + /** Forward this call to the base class but save and restore the + currently selected item. + Assign the given master page to the list of pages. + @param pMasterPage + This master page will usually be a member of the list of all + available master pages as provided by the MasterPageContainer. + @param rPageList + The pages to which to assign the master page. These pages may + be slides or master pages themselves. + */ + virtual void AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr<std::vector<SdPage*> >& rpPageList) override; + + virtual void LateInit() override; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx b/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx new file mode 100644 index 000000000..268658823 --- /dev/null +++ b/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx @@ -0,0 +1,366 @@ +/* -*- 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 "RecentlyUsedMasterPages.hxx" +#include "MasterPageContainerProviders.hxx" +#include <MasterPageObserver.hxx> +#include "MasterPageDescriptor.hxx" +#include <tools/ConfigurationAccess.hxx> + +#include <algorithm> +#include <memory> +#include <vector> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <osl/doublecheckedlocking.h> +#include <osl/getglobalmutex.hxx> + +using namespace ::std; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +OUString GetPathToImpressConfigurationRoot() +{ + return "/org.openoffice.Office.Impress/"; +} +OUString GetPathToSetNode() +{ + return "MultiPaneGUI/ToolPanel/RecentlyUsedMasterPages"; +} + +} // end of anonymous namespace + +namespace sd::sidebar { + +RecentlyUsedMasterPages* RecentlyUsedMasterPages::mpInstance = nullptr; + +RecentlyUsedMasterPages& RecentlyUsedMasterPages::Instance() +{ + if (mpInstance == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (mpInstance == nullptr) + { + RecentlyUsedMasterPages* pInstance = new RecentlyUsedMasterPages(); + pInstance->LateInit(); + SdGlobalResourceContainer::Instance().AddResource ( + ::std::unique_ptr<SdGlobalResource>(pInstance)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + mpInstance = pInstance; + } + } + else { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + return *mpInstance; +} + +constexpr size_t gnMaxListSize(8); + +RecentlyUsedMasterPages::RecentlyUsedMasterPages() + : mpContainer(std::make_shared<MasterPageContainer>()) +{ +} + +RecentlyUsedMasterPages::~RecentlyUsedMasterPages() +{ + Link<MasterPageContainerChangeEvent&,void> aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); + mpContainer->RemoveChangeListener(aLink); + + MasterPageObserver::Instance().RemoveEventListener( + LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); +} + +void RecentlyUsedMasterPages::LateInit() +{ + Link<MasterPageContainerChangeEvent&,void> aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); + mpContainer->AddChangeListener(aLink); + + LoadPersistentValues (); + MasterPageObserver::Instance().AddEventListener( + LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); +} + +void RecentlyUsedMasterPages::LoadPersistentValues() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + GetPathToImpressConfigurationRoot(), + tools::ConfigurationAccess::READ_ONLY); + Reference<container::XNameAccess> xSet ( + aConfiguration.GetConfigurationNode(GetPathToSetNode()), + UNO_QUERY); + if ( ! xSet.is()) + return; + + static const OUStringLiteral sURLMemberName(u"URL"); + static const OUStringLiteral sNameMemberName(u"Name"); + OUString sURL; + OUString sName; + + // Read the names and URLs of the master pages. + const Sequence<OUString> aKeys (xSet->getElementNames()); + mvMasterPages.clear(); + mvMasterPages.reserve(aKeys.getLength()); + for (const auto& rKey : aKeys) + { + Reference<container::XNameAccess> xSetItem ( + xSet->getByName(rKey), UNO_QUERY); + if (xSetItem.is()) + { + Any aURL (xSetItem->getByName(sURLMemberName)); + Any aName (xSetItem->getByName(sNameMemberName)); + aURL >>= sURL; + aName >>= sName; + SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>( + MasterPageContainer::TEMPLATE, + -1, + sURL, + OUString(), + sName, + false, + std::make_shared<TemplatePageObjectProvider>(sURL), + std::make_shared<TemplatePreviewProvider>(sURL)); + // For user supplied templates we use a different + // preview provider: The preview in the document shows + // not only shapes on the master page but also shapes on + // the foreground. This is misleading and therefore + // these previews are discarded and created directly + // from the page objects. + if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) + pDescriptor->mpPreviewProvider = std::make_shared<PagePreviewProvider>(); + MasterPageContainer::Token aToken (mpContainer->PutMasterPage(pDescriptor)); + mvMasterPages.emplace_back(aToken,sURL,sName); + } + } + + ResolveList(); + } + catch (Exception&) + { + // Ignore exception. + } +} + +void RecentlyUsedMasterPages::SavePersistentValues() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + GetPathToImpressConfigurationRoot(), + tools::ConfigurationAccess::READ_WRITE); + Reference<container::XNameContainer> xSet ( + aConfiguration.GetConfigurationNode(GetPathToSetNode()), + UNO_QUERY); + if ( ! xSet.is()) + return; + + // Clear the set. + const Sequence<OUString> aKeys (xSet->getElementNames()); + for (const auto& rKey : aKeys) + xSet->removeByName (rKey); + + // Fill it with the URLs of this object. + static const OUStringLiteral sURLMemberName(u"URL"); + static const OUStringLiteral sNameMemberName(u"Name"); + Any aValue; + Reference<lang::XSingleServiceFactory> xChildFactory ( + xSet, UNO_QUERY); + if ( ! xChildFactory.is()) + return; + sal_Int32 nIndex(0); + for (const auto& rDescriptor : mvMasterPages) + { + // Create new child. + OUString sKey = "index_" + OUString::number(nIndex); + Reference<container::XNameReplace> xChild( + xChildFactory->createInstance(), UNO_QUERY); + if (xChild.is()) + { + xSet->insertByName (sKey, Any(xChild)); + + aValue <<= rDescriptor.msURL; + xChild->replaceByName (sURLMemberName, aValue); + + aValue <<= rDescriptor.msName; + xChild->replaceByName (sNameMemberName, aValue); + } + ++nIndex; + } + + // Write the data back to disk. + aConfiguration.CommitChanges(); + } + catch (Exception&) + { + // Ignore exception. + } +} + +void RecentlyUsedMasterPages::AddEventListener (const Link<LinkParamNone*,void>& rEventListener) +{ + if (::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener) == maListeners.end()) + { + maListeners.push_back (rEventListener); + } +} + +void RecentlyUsedMasterPages::RemoveEventListener (const Link<LinkParamNone*,void>& rEventListener) +{ + maListeners.erase ( + ::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener)); +} + +int RecentlyUsedMasterPages::GetMasterPageCount() const +{ + return mvMasterPages.size(); +} + +MasterPageContainer::Token RecentlyUsedMasterPages::GetTokenForIndex (sal_uInt32 nIndex) const +{ + if(nIndex<mvMasterPages.size()) + return mvMasterPages[nIndex].maToken; + else + return MasterPageContainer::NIL_TOKEN; +} + +void RecentlyUsedMasterPages::SendEvent() +{ + for (const auto& aLink : maListeners) + { + aLink.Call(nullptr); + } +} + +IMPL_LINK(RecentlyUsedMasterPages, MasterPageChangeListener, + MasterPageObserverEvent&, rEvent, void) +{ + switch (rEvent.meType) + { + case MasterPageObserverEvent::ET_MASTER_PAGE_ADDED: + case MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS: + AddMasterPage( + mpContainer->GetTokenForStyleName(rEvent.mrMasterPageName)); + break; + + case MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED: + // Do not change the list of recently master pages (the deleted + // page was recently used) but tell the listeners. They may want + // to update their lists. + SendEvent(); + break; + } +} + +IMPL_LINK(RecentlyUsedMasterPages, MasterPageContainerChangeListener, + MasterPageContainerChangeEvent&, rEvent, void) +{ + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::CHILD_ADDED: + case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED: + case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED: + ResolveList(); + break; + + default: + // Ignored. + break; + } +} + +void RecentlyUsedMasterPages::AddMasterPage ( + MasterPageContainer::Token aToken) +{ + // For the page to be inserted the token has to be valid and the page + // has to have a valid URL. This excludes master pages that do not come + // from template files. + if (aToken == MasterPageContainer::NIL_TOKEN + || mpContainer->GetURLForToken(aToken).isEmpty()) + return; + + MasterPageList::iterator aIterator ( + ::std::find_if(mvMasterPages.begin(),mvMasterPages.end(), + Descriptor::TokenComparator(aToken))); + if (aIterator != mvMasterPages.end()) + { + // When an entry for the given token already exists then remove + // it now and insert it later at the head of the list. + mvMasterPages.erase (aIterator); + } + + mvMasterPages.insert(mvMasterPages.begin(), + Descriptor( + aToken, + mpContainer->GetURLForToken(aToken), + mpContainer->GetStyleNameForToken(aToken))); + + // Shorten list to maximal size. + while (mvMasterPages.size() > gnMaxListSize) + { + mvMasterPages.pop_back (); + } + + SavePersistentValues (); + SendEvent(); +} + +void RecentlyUsedMasterPages::ResolveList() +{ + bool bNotify (false); + + for (auto& rDescriptor : mvMasterPages) + { + if (rDescriptor.maToken == MasterPageContainer::NIL_TOKEN) + { + MasterPageContainer::Token aToken (mpContainer->GetTokenForURL(rDescriptor.msURL)); + rDescriptor.maToken = aToken; + if (aToken != MasterPageContainer::NIL_TOKEN) + bNotify = true; + } + else + { + if ( ! mpContainer->HasToken(rDescriptor.maToken)) + { + rDescriptor.maToken = MasterPageContainer::NIL_TOKEN; + bNotify = true; + } + } + } + + if (bNotify) + SendEvent(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx b/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx new file mode 100644 index 000000000..e95e66ccb --- /dev/null +++ b/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx @@ -0,0 +1,125 @@ +/* -*- 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 . + */ + +#pragma once + +#include <tools/SdGlobalResourceContainer.hxx> +#include <tools/link.hxx> +#include <vector> + +#include "MasterPageContainer.hxx" + +namespace sd { +class MasterPageObserverEvent; +} + +namespace sd::sidebar { + +/** This singleton holds a list of the most recently used master pages. +*/ +class RecentlyUsedMasterPages + : public SdGlobalResource +{ +public: + /** Return the single instance of this class. + */ + static RecentlyUsedMasterPages& Instance(); + + void AddEventListener (const Link<LinkParamNone*,void>& rEventListener); + void RemoveEventListener (const Link<LinkParamNone*,void>& rEventListener); + + int GetMasterPageCount() const; + MasterPageContainer::Token GetTokenForIndex (sal_uInt32 nIndex) const; + +private: + class Descriptor + { + public: + OUString msURL; + OUString msName; + ::sd::sidebar::MasterPageContainer::Token maToken; + Descriptor (::sd::sidebar::MasterPageContainer::Token aToken, + const OUString& rsURL, const OUString& rsName) + : msURL(rsURL), + msName(rsName), + maToken(aToken) + {} + + class TokenComparator + { + public: + explicit TokenComparator(::sd::sidebar::MasterPageContainer::Token aToken) + : maToken(aToken) {} + bool operator () (const Descriptor& rDescriptor) + { return maToken==rDescriptor.maToken; } + + private: + ::sd::sidebar::MasterPageContainer::Token maToken; + }; + }; + + /** The single instance of this class. It is created on demand when + Instance() is called for the first time. + */ + static RecentlyUsedMasterPages* mpInstance; + + ::std::vector<Link<LinkParamNone*,void>> maListeners; + + typedef ::std::vector<Descriptor> MasterPageList; + MasterPageList mvMasterPages; + std::shared_ptr<MasterPageContainer> mpContainer; + + RecentlyUsedMasterPages(); + virtual ~RecentlyUsedMasterPages() override; + + /** Call this method after a new object has been created. + */ + void LateInit(); + + RecentlyUsedMasterPages (const RecentlyUsedMasterPages&) = delete; + + RecentlyUsedMasterPages& operator= (const RecentlyUsedMasterPages&) = delete; + + void SendEvent(); + DECL_LINK(MasterPageChangeListener, MasterPageObserverEvent&, void); + DECL_LINK(MasterPageContainerChangeListener, MasterPageContainerChangeEvent&, void); + + /** Add a descriptor for the specified master page to the end of the + list of most recently used master pages. When the page is already a + member of that list the associated descriptor is moved to the end of + the list to make it the most recently used entry. + */ + void AddMasterPage(MasterPageContainer::Token aToken); + + /** Load the list of recently used master pages from the registry where + it was saved to make it persistent. + */ + void LoadPersistentValues(); + + /** Save the list of recently used master pages to the registry to make + it persistent. + */ + void SavePersistentValues(); + + void ResolveList(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/SlideBackground.cxx b/sd/source/ui/sidebar/SlideBackground.cxx new file mode 100644 index 000000000..742275bab --- /dev/null +++ b/sd/source/ui/sidebar/SlideBackground.cxx @@ -0,0 +1,1286 @@ +/* -*- 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 <com/sun/star/ui/XDeck.hpp> +#include <com/sun/star/ui/XPanel.hpp> +#include <com/sun/star/frame/XController2.hpp> +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include "SlideBackground.hxx" +#include <sdresid.hxx> +#include <ViewShellBase.hxx> +#include <FrameView.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include "PageMarginUtils.hxx" +#include <strings.hrc> +#include <pageformatpanel.hrc> +#include <DrawViewShell.hxx> +#include <svl/intitem.hxx> +#include <svx/colorbox.hxx> +#include <svx/dlgutil.hxx> +#include <svx/drawitem.hxx> +#include <svx/itemwin.hxx> +#include <svx/pageitem.hxx> +#include <app.hrc> +#include <editeng/paperinf.hxx> +#include <svx/xflgrit.hxx> +#include <svx/rulritem.hxx> +#include <svx/svxids.hrc> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xgrad.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/svdpage.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sidebar/Panel.hxx> +#include <EventMultiplexer.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/EnumContext.hxx> +#include <vcl/svapp.hxx> + +#include <editeng/sizeitem.hxx> +#include <comphelper/lok.hxx> +#include <unomodel.hxx> +#include <sfx2/lokhelper.hxx> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + +namespace sd::sidebar { + +namespace { + +enum eFillStyle +{ + NONE, + SOLID, + GRADIENT, + HATCH, + BITMAP, + PATTERN +}; + +} + +SlideBackground::SlideBackground( + weld::Widget* pParent, + ViewShellBase& rBase, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings) : + PanelLayout( pParent, "SlideBackgroundPanel", "modules/simpress/ui/sidebarslidebackground.ui" ), + mrBase( rBase ), + mxPaperSizeBox(new SvxPaperSizeListBox(m_xBuilder->weld_combo_box("paperformat"))), + mxPaperOrientation(m_xBuilder->weld_combo_box("orientation")), + mxMasterSlide(m_xBuilder->weld_combo_box("masterslide")), + mxBackgroundLabel(m_xBuilder->weld_label("label3")), + mxFillStyle(m_xBuilder->weld_combo_box("fillstyle")), + mxFillLB(new ColorListBox(m_xBuilder->weld_menu_button("fillattr"), [this]{ return GetFrameWeld(); })), + mxFillAttr(m_xBuilder->weld_combo_box("fillattr1")), + mxFillGrad1(new ColorListBox(m_xBuilder->weld_menu_button("fillattr2"), [this]{ return GetFrameWeld(); })), + mxFillGrad2(new ColorListBox(m_xBuilder->weld_menu_button("fillattr3"), [this]{ return GetFrameWeld(); })), + mxInsertImage(m_xBuilder->weld_button("button2")), + mxDspMasterBackground(m_xBuilder->weld_check_button("displaymasterbackground")), + mxDspMasterObjects(m_xBuilder->weld_check_button("displaymasterobjects")), + mxCloseMaster(m_xBuilder->weld_button("closemasterslide")), + mxEditMaster(m_xBuilder->weld_button("masterslidebutton")), + mxMasterLabel(m_xBuilder->weld_label("masterlabel")), + mxMarginSelectBox(m_xBuilder->weld_combo_box("marginLB")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")), + mxMarginLabel(m_xBuilder->weld_label("labelmargin")), + maPaperSizeController(SID_ATTR_PAGE_SIZE, *pBindings, *this), + maPaperOrientationController(SID_ATTR_PAGE, *pBindings, *this), + maPaperMarginLRController(SID_ATTR_PAGE_LRSPACE, *pBindings, *this), + maPaperMarginULController(SID_ATTR_PAGE_ULSPACE, *pBindings, *this), + maBckColorController(SID_ATTR_PAGE_COLOR, *pBindings, *this), + maBckGradientController(SID_ATTR_PAGE_GRADIENT, *pBindings, *this), + maBckHatchController(SID_ATTR_PAGE_HATCH, *pBindings, *this), + maBckBitmapController(SID_ATTR_PAGE_BITMAP, *pBindings, *this), + maBckFillStyleController(SID_ATTR_PAGE_FILLSTYLE, *pBindings, *this), + maBckImageController(SID_SELECT_BACKGROUND, *pBindings, *this), + maDspBckController(SID_DISPLAY_MASTER_BACKGROUND, *pBindings, *this), + maDspObjController(SID_DISPLAY_MASTER_OBJECTS, *pBindings, *this), + maMetricController(SID_ATTR_METRIC, *pBindings, *this), + maCloseMasterController(SID_CLOSE_MASTER_VIEW, *pBindings, *this), + mpPageItem( new SvxPageItem(SID_ATTR_PAGE) ), + mbSwitchModeToNormal(false), + mbSwitchModeToMaster(false), + mxFrame(rxFrame), + maDrawOtherContext(vcl::EnumContext::Application::Draw, vcl::EnumContext::Context::DrawPage), + maDrawMasterContext(vcl::EnumContext::Application::Draw, vcl::EnumContext::Context::MasterPage), + maImpressOtherContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::DrawPage), + maImpressMasterContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::MasterPage), + maImpressHandoutContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::HandoutPage), + maImpressNotesContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::NotesPage), + mbTitle(false), + mpPageLRMarginItem( new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ) ), + mpPageULMarginItem( new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ) ), + m_nPageLeftMargin(0), + m_nPageRightMargin(0), + m_nPageTopMargin(0), + m_nPageBottomMargin(0), + meFUnit(GetModuleFieldUnit()), + mpBindings(pBindings) +{ + //let the listbox shrink to any size so the sidebar isn't forced to grow to + //the size of the longest master slide name in the document + mxMasterSlide->set_size_request(42, -1); + + maCustomEntry = mxCustomEntry->get_label(); + + addListener(); + Initialize(); +} + +bool SlideBackground::IsDraw() +{ + return ( maContext == maDrawMasterContext || + maContext == maDrawOtherContext ); +} + +bool SlideBackground::IsImpress() +{ + return ( maContext == maImpressMasterContext || + maContext == maImpressOtherContext || + maContext == maImpressHandoutContext || + maContext == maImpressNotesContext ); +} + +FieldUnit SlideBackground::GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState) +{ + FieldUnit eUnit; + + if (pState && eState >= SfxItemState::DEFAULT) + eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pState)->GetValue()); + else + eUnit = GetModuleFieldUnit(); + + return eUnit; +} + +void SlideBackground::SetMarginsFieldUnit() +{ + auto nSelected = mxMarginSelectBox->get_active(); + mxMarginSelectBox->clear(); + + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + + if (IsInch(meFUnit)) + { + OUString sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_INCH); ++i) + { + OUString sMeasurement = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_INCH[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SdResId(RID_PAGEFORMATPANEL_MARGINS_INCH[i].first).replaceFirst("%1", sMeasurement)); + } + } + else + { + OUString sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_CM); ++i) + { + OUString sMeasurement = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_CM[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SdResId(RID_PAGEFORMATPANEL_MARGINS_CM[i].first).replaceFirst("%1", sMeasurement)); + } + } + + mxMarginSelectBox->set_active(nSelected); +} + +void SlideBackground::Initialize() +{ + SvxFillTypeBox::Fill(*mxFillStyle); + + SetMarginsFieldUnit(); + + mxPaperSizeBox->FillPaperSizeEntries( PaperSizeApp::Draw ); + mxPaperSizeBox->connect_changed(LINK(this,SlideBackground,PaperSizeModifyHdl)); + mxPaperOrientation->connect_changed(LINK(this,SlideBackground,PaperSizeModifyHdl)); + mxEditMaster->connect_clicked(LINK(this, SlideBackground, EditMasterHdl)); + mxCloseMaster->connect_clicked(LINK(this, SlideBackground, CloseMasterHdl)); + mxInsertImage->connect_clicked(LINK(this, SlideBackground, SelectBgHdl)); + meUnit = maPaperSizeController.GetCoreMetric(); + + mxMasterSlide->connect_changed(LINK(this, SlideBackground, AssignMasterPage)); + + mxFillStyle->connect_changed(LINK(this, SlideBackground, FillStyleModifyHdl)); + mxFillLB->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillGrad1->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillGrad2->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillAttr->connect_changed(LINK(this, SlideBackground, FillBackgroundHdl)); + + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell) + { + FrameView *pFrameView = pMainViewShell->GetFrameView(); + + if ( pFrameView->GetViewShEditMode() == EditMode::Page ) + { + SdPage* mpPage = pMainViewShell->getCurrentPage(); + populateMasterSlideDropdown(); + + OUString aLayoutName( mpPage->GetLayoutName() ); + aLayoutName = aLayoutName.copy(0,aLayoutName.indexOf(SD_LT_SEPARATOR)); + mxMasterSlide->set_active_text(aLayoutName); + } + } + + mxFillStyle->set_active(static_cast< sal_Int32 >(NONE)); + + mxDspMasterBackground->connect_toggled(LINK(this, SlideBackground, DspBackground)); + mxDspMasterObjects->connect_toggled(LINK(this, SlideBackground, DspObjects)); + + //margins + mxMarginSelectBox->connect_changed(LINK(this, SlideBackground, ModifyMarginHdl)); + + Update(); + UpdateMarginBox(); +} + +void SlideBackground::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter) +{ + if (mxPaperSizeBox->get_active() == -1) + { + mpBindings->Update(SID_ATTR_PAGE_SIZE); + } + + PanelLayout::DumpAsPropertyTree(rJsonWriter); +} + +void SlideBackground::HandleContextChange( + const vcl::EnumContext& rContext) +{ + if (maContext == rContext) + return; + maContext = rContext; + + if ( IsImpress() ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERSLIDE_LABEL)); + + // margin selector is only for Draw + mxMarginSelectBox->hide(); + mxMarginLabel->hide(); + + if ( maContext == maImpressMasterContext ) + { + mxCloseMaster->show(); + mxEditMaster->hide(); + mxMasterSlide->set_sensitive(false); + mxMasterSlide->clear(); + mxDspMasterBackground->set_sensitive(false); + mxDspMasterObjects->set_sensitive(false); + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + mxInsertImage->show(); + + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + } + else if ( maContext == maImpressHandoutContext || maContext == maImpressNotesContext ) + { + mxCloseMaster->hide(); + mxEditMaster->hide(); + mxMasterSlide->set_sensitive(false); + mxMasterSlide->clear(); + mxDspMasterBackground->set_sensitive(false); + mxDspMasterObjects->set_sensitive(false); + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + mxInsertImage->hide(); + } + else if (maContext == maImpressOtherContext) + { + mxCloseMaster->hide(); + mxEditMaster->show(); + mxMasterSlide->set_sensitive(true); + populateMasterSlideDropdown(); + mxDspMasterBackground->set_sensitive(true); + mxDspMasterObjects->set_sensitive(true); + mxFillStyle->show(); + mxBackgroundLabel->show(); + mxInsertImage->show(); + } + + // Need to do a relayouting, otherwise the panel size is not updated after show / hide controls + if (m_pPanel) + m_pPanel->TriggerDeckLayouting(); + + } + else if ( IsDraw() ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERPAGE_LABEL)); + mxDspMasterBackground->hide(); + mxDspMasterObjects->hide(); + + if (maContext == maDrawOtherContext) + { + mxEditMaster->hide(); + mxFillStyle->show(); + mxBackgroundLabel->show(); + } + else if (maContext == maDrawMasterContext) + { + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + } + } + + // The Insert Image button in the sidebar issues .uno:SelectBackground, + // which when invoked without arguments will open the file-open-dialog + // to prompt the user to select a file. This is useless in LOOL. + // Hide for now so the user will only be able to use the menu to insert + // background image, which prompts the user for file selection in the browser. + if (comphelper::LibreOfficeKit::isActive()) + mxInsertImage->hide(); +} + +void SlideBackground::Update() +{ + eFillStyle nPos = static_cast<eFillStyle>(mxFillStyle->get_active()); + + if(maContext != maImpressOtherContext && maContext != maDrawOtherContext) + nPos = NONE; + + SfxObjectShell* pSh = SfxObjectShell::Current(); + if (!pSh) + return; + + switch(nPos) + { + case NONE: + { + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + } + break; + case SOLID: + { + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + mxFillLB->show(); + const Color aColor = GetColorSetOrDefault(); + mxFillLB->SelectEntry(aColor); + } + break; + case GRADIENT: + { + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->show(); + mxFillGrad2->show(); + + const XGradient xGradient = GetGradientSetOrDefault(); + const Color aStartColor = xGradient.GetStartColor(); + mxFillGrad1->SelectEntry(aStartColor); + const Color aEndColor = xGradient.GetEndColor(); + mxFillGrad2->SelectEntry(aEndColor); + } + break; + + case HATCH: + { + mxFillLB->hide(); + mxFillAttr->show(); + mxFillAttr->clear(); + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList()); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + + const OUString aHatchName = GetHatchingSetOrDefault(); + mxFillAttr->set_active_text( aHatchName ); + } + break; + + case BITMAP: + case PATTERN: + { + mxFillLB->hide(); + mxFillAttr->show(); + mxFillAttr->clear(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + OUString aName; + if(nPos == BITMAP) + { + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList()); + aName = GetBitmapSetOrDefault(); + } + else if(nPos == PATTERN) + { + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList()); + aName = GetPatternSetOrDefault(); + } + mxFillAttr->set_active_text( aName ); + } + break; + default: + break; + } + + // Need to do a relayouting, otherwise the panel size is not updated after show / hide controls + if (m_pPanel) + m_pPanel->TriggerDeckLayouting(); +} + +void SlideBackground::UpdateMarginBox() +{ + m_nPageLeftMargin = mpPageLRMarginItem->GetLeft(); + m_nPageRightMargin = mpPageLRMarginItem->GetRight(); + m_nPageTopMargin = mpPageULMarginItem->GetUpper(); + m_nPageBottomMargin = mpPageULMarginItem->GetLower(); + + int nCustomIndex = mxMarginSelectBox->find_text(maCustomEntry); + + if( IsNone(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(0); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNarrow(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(1); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsModerate(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(2); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal075(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(3); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal100(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(4); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal125(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(5); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsWide(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(6); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else + { + if (nCustomIndex == -1) + mxMarginSelectBox->append_text(maCustomEntry); + mxMarginSelectBox->set_active_text(maCustomEntry); + } +} + +void SlideBackground::SetPanelTitle( const OUString& rTitle ) +{ + Reference<frame::XController2> xController( mxFrame->getController(), uno::UNO_QUERY); + if ( !xController.is() ) + return; + + Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); + if ( !xSidebarProvider.is() ) + return; + + Reference<ui::XDecks> xDecks = xSidebarProvider->getDecks(); + if ( !xDecks.is() ) + return; + + Reference<ui::XDeck> xDeck ( xDecks->getByName("PropertyDeck"), uno::UNO_QUERY); + if ( !xDeck.is() ) + return; + + Reference<ui::XPanels> xPanels = xDeck->getPanels(); + if ( !xPanels.is() ) + return; + + if (xPanels->hasByName("SlideBackgroundPanel")) + { + Reference<ui::XPanel> xPanel ( xPanels->getByName("SlideBackgroundPanel"), uno::UNO_QUERY); + if ( !xPanel.is() ) + return; + + xPanel->setTitle( rTitle ); + } +} + +void SlideBackground::addListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this, SlideBackground, EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void SlideBackground::removeListener() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this, SlideBackground, EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(SlideBackground, EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + // add more events as per requirement + // Master Page change triggers a shape change event. Solves sync problem. + case EventMultiplexerEventId::ShapeChanged: + populateMasterSlideDropdown(); + break; + case EventMultiplexerEventId::EditModeNormal: + mbSwitchModeToNormal = true; + break; + case EventMultiplexerEventId::EditModeMaster: + mbSwitchModeToMaster = true; + break; + case EventMultiplexerEventId::EditViewSelection: + case EventMultiplexerEventId::EndTextEdit: + { + if ( mbSwitchModeToMaster ) + { + if( IsImpress() ) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_MASTERPAGE_NAME)); + mbSwitchModeToMaster = false; + } + else if ( mbSwitchModeToNormal ) + { + if( IsImpress() ) + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_PAGE_NAME)); + mbSwitchModeToNormal = false; + } + + } + break; + case EventMultiplexerEventId::CurrentPageChanged: + { + static const sal_uInt16 SidArray[] = { + SID_ATTR_PAGE_COLOR, + SID_ATTR_PAGE_GRADIENT, + SID_ATTR_PAGE_HATCH, + SID_ATTR_PAGE_BITMAP, + SID_ATTR_PAGE_FILLSTYLE, + SID_DISPLAY_MASTER_BACKGROUND, + SID_DISPLAY_MASTER_OBJECTS, + 0 }; + updateMasterSlideSelection(); + GetBindings()->Invalidate( SidArray ); + } + break; + case EventMultiplexerEventId::ViewAdded: + { + if(!mbTitle) + { + if( IsDraw() ) + { + mxCloseMaster->hide(); + mxEditMaster->hide(); + if( maContext == maDrawMasterContext) + SetPanelTitle(SdResId(STR_MASTERPAGE_NAME)); + else + SetPanelTitle(SdResId(STR_PAGE_NAME)); + } + else if ( maContext == maImpressOtherContext || maContext == maImpressMasterContext ) + { + if( maContext == maImpressMasterContext ) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + } + else if ( maContext == maImpressNotesContext ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERSLIDE_LABEL)); + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + + if (pMainViewShell) + { + DrawViewShell* pDrawViewShell = static_cast<DrawViewShell*>(pMainViewShell); + if ( pDrawViewShell->GetEditMode() == EditMode::MasterPage) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else // EditMode::Page + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + } + } + mbTitle = true; + } + } + break; + default: + break; + } +} + +void SlideBackground::populateMasterSlideDropdown() +{ + mxMasterSlide->clear(); + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell*>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = pDocSh ? pDocSh->GetDoc() : nullptr; + sal_uInt16 nCount = pDoc ? pDoc->GetMasterPageCount() : 0; + for( sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++ ) + { + SdPage* pMaster = static_cast<SdPage*>(pDoc->GetMasterPage(nLayout)); + if( pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0,aLayoutName.indexOf(SD_LT_SEPARATOR)); + mxMasterSlide->append_text(aLayoutName); + } + } + updateMasterSlideSelection(); +} + +void SlideBackground::updateMasterSlideSelection() +{ + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + SdPage* pPage = pMainViewShell ? pMainViewShell->getCurrentPage() : nullptr; + if (pPage != nullptr && pPage->TRG_HasMasterPage()) + { + SdrPage& rMasterPage (pPage->TRG_GetMasterPage()); + SdPage* pMasterPage = static_cast<SdPage*>(&rMasterPage); + mxMasterSlide->set_active_text(pMasterPage->GetName()); + } +} + +SlideBackground::~SlideBackground() +{ + removeListener(); + + mxCustomEntry.reset(); + mxMarginLabel.reset(); + mxPaperSizeBox.reset(); + mxPaperOrientation.reset(); + mxMasterSlide.reset(); + mxBackgroundLabel.reset(); + mxFillAttr.reset(); + mxFillGrad1.reset(); + mxFillGrad2.reset(); + mxFillStyle.reset(); + mxFillLB.reset(); + mxInsertImage.reset(); + mxMarginSelectBox.reset(); + mxDspMasterBackground.reset(); + mxDspMasterObjects.reset(); + mxMasterLabel.reset(); + mxEditMaster.reset(); + mxCloseMaster.reset(); + + maPaperSizeController.dispose(); + maPaperOrientationController.dispose(); + maPaperMarginLRController.dispose(); + maPaperMarginULController.dispose(); + maBckColorController.dispose(); + maBckGradientController.dispose(); + maBckHatchController.dispose(); + maBckBitmapController.dispose(); + maBckFillStyleController.dispose(); + maBckImageController.dispose(); + maDspBckController.dispose(); + maDspObjController.dispose(); + maMetricController.dispose(); + maCloseMasterController.dispose(); + + mpPageItem.reset(); + mpColorItem.reset(); + mpHatchItem.reset(); + mpBitmapItem.reset(); + mpPageLRMarginItem.reset(); + mpPageULMarginItem.reset(); +} + +void SlideBackground::ExecuteMarginLRChange(const ::tools::Long mnPageLeftMargin, const ::tools::Long mnPageRightMargin) +{ + mpPageLRMarginItem->SetLeft(mnPageLeftMargin); + mpPageLRMarginItem->SetRight(mnPageRightMargin); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_LRSPACE, SfxCallMode::RECORD, { mpPageLRMarginItem.get() } ); +} + +void SlideBackground::ExecuteMarginULChange(const ::tools::Long mnPageTopMargin, const ::tools::Long mnPageBottomMargin) +{ + mpPageULMarginItem->SetUpper(mnPageTopMargin); + mpPageULMarginItem->SetLower(mnPageBottomMargin); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_ULSPACE, SfxCallMode::RECORD, { mpPageULMarginItem.get() } ); +} + +Color const & SlideBackground::GetColorSetOrDefault() +{ + // Tango Sky Blue 1, to be consistent w/ area fill panel (b/c COL_AUTO for slides is transparent) + if ( !mpColorItem ) + mpColorItem.reset( new XFillColorItem( OUString(), Color(0x72, 0x9f, 0xcf) ) ); + + return mpColorItem->GetColorValue(); +} + +XGradient const & SlideBackground::GetGradientSetOrDefault() +{ + if( !mpGradientItem ) + { + XGradient aGradient; + OUString aGradientName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxGradientListItem * pGradListItem = pSh->GetItem(SID_GRADIENT_LIST); + aGradient = pGradListItem->GetGradientList()->GetGradient(0)->GetGradient(); + aGradientName = pGradListItem->GetGradientList()->GetGradient(0)->GetName(); + } + mpGradientItem.reset( new XFillGradientItem( aGradientName, aGradient ) ); + } + + return mpGradientItem->GetGradientValue(); +} + +OUString const & SlideBackground::GetHatchingSetOrDefault() +{ + if( !mpHatchItem ) + { + XHatch aHatch; + OUString aHatchName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + aHatch = pHatchListItem->GetHatchList()->GetHatch(0)->GetHatch(); + aHatchName = pHatchListItem->GetHatchList()->GetHatch(0)->GetName(); + } + mpHatchItem.reset( new XFillHatchItem( aHatchName, aHatch ) ); + } + + return mpHatchItem->GetName(); +} + +OUString const & SlideBackground::GetBitmapSetOrDefault() +{ + if( !mpBitmapItem || mpBitmapItem->isPattern()) + { + GraphicObject aGraphObj; + OUString aBmpName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxBitmapListItem * pBmpListItem = pSh->GetItem(SID_BITMAP_LIST); + aGraphObj = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetGraphicObject(); + aBmpName = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetName(); + } + mpBitmapItem.reset( new XFillBitmapItem( aBmpName, aGraphObj ) ); + } + + return mpBitmapItem->GetName(); +} + +OUString const & SlideBackground::GetPatternSetOrDefault() +{ + if( !mpBitmapItem || !(mpBitmapItem->isPattern())) + { + GraphicObject aGraphObj; + OUString aPtrnName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxPatternListItem * pPtrnListItem = pSh->GetItem(SID_PATTERN_LIST); + aGraphObj = pPtrnListItem->GetPatternList()->GetBitmap(0)->GetGraphicObject(); + aPtrnName = pPtrnListItem->GetPatternList()->GetBitmap(0)->GetName(); + } + mpBitmapItem.reset( new XFillBitmapItem( aPtrnName, aGraphObj ) ); + } + + return mpBitmapItem->GetName(); +} + +void SlideBackground::NotifyItemUpdate( + const sal_uInt16 nSID, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + switch(nSID) + { + + case SID_ATTR_PAGE_COLOR: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(SOLID)); + mpColorItem.reset(pState ? static_cast< XFillColorItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_HATCH: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(HATCH)); + mpHatchItem.reset(pState ? static_cast < XFillHatchItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32>(GRADIENT)); + mpGradientItem.reset(pState ? static_cast< XFillGradientItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + case SID_ATTR_PAGE_BITMAP: + { + if(eState >= SfxItemState::DEFAULT) + { + mpBitmapItem.reset(pState ? static_cast< XFillBitmapItem* >(pState->Clone()) : nullptr); + if(mpBitmapItem) + { + if(mpBitmapItem->isPattern()) + mxFillStyle->set_active(static_cast< sal_Int32 >(PATTERN)); + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + } + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + Update(); + } + } + break; + + case SID_ATTR_PAGE_FILLSTYLE: + { + const XFillStyleItem* pFillStyleItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pFillStyleItem = dynamic_cast< const XFillStyleItem* >(pState); + if (pFillStyleItem) + { + css::drawing::FillStyle eXFS = pFillStyleItem->GetValue(); + switch(eXFS) + { + case drawing::FillStyle_NONE: + mxFillStyle->set_active(static_cast< sal_Int32 >(NONE)); + break; + case drawing::FillStyle_SOLID: + mxFillStyle->set_active(static_cast< sal_Int32 >(SOLID)); + break; + case drawing::FillStyle_GRADIENT: + mxFillStyle->set_active(static_cast< sal_Int32 >(GRADIENT)); + break; + case drawing::FillStyle_HATCH: + mxFillStyle->set_active(static_cast< sal_Int32 >(HATCH)); + break; + case drawing::FillStyle_BITMAP: + { + if(mpBitmapItem->isPattern()) + mxFillStyle->set_active(static_cast< sal_Int32 >(PATTERN)); + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + } + break; + default: + break; + } + Update(); + } + } + break; + + case SID_ATTR_PAGE_SIZE: + { + const SvxSizeItem* pSizeItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pSizeItem = dynamic_cast<const SvxSizeItem*>(pState); + if (pSizeItem) + { + Size aPaperSize = pSizeItem->GetSize(); + if (mxPaperOrientation->get_active() == 0) + Swap(aPaperSize); + + Paper ePaper = SvxPaperInfo::GetSvxPaper(aPaperSize, meUnit); + mxPaperSizeBox->set_active_id( ePaper ); + } + } + break; + + case SID_ATTR_PAGE: + { + const SvxPageItem* pPageItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pPageItem = dynamic_cast<const SvxPageItem*>(pState); + if (pPageItem) + { + mpPageItem.reset(pPageItem->Clone()); + bool bIsLandscape = mpPageItem->IsLandscape(); + mxPaperOrientation->set_active( bIsLandscape ? 0 : 1 ); + } + } + break; + + case SID_ATTR_PAGE_LRSPACE: + { + const SvxLongLRSpaceItem* pLRItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pLRItem = dynamic_cast<const SvxLongLRSpaceItem*>(pState); + if (pLRItem) + { + mpPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + + case SID_ATTR_PAGE_ULSPACE: + { + const SvxLongULSpaceItem* pULItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pULItem = dynamic_cast<const SvxLongULSpaceItem*>(pState); + if (pULItem) + { + mpPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + + case SID_DISPLAY_MASTER_BACKGROUND: + { + const SfxBoolItem* pBoolItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pBoolItem = dynamic_cast< const SfxBoolItem* >(pState); + if (pBoolItem) + mxDspMasterBackground->set_active(pBoolItem->GetValue()); + } + break; + case SID_DISPLAY_MASTER_OBJECTS: + { + const SfxBoolItem* pBoolItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pBoolItem = dynamic_cast< const SfxBoolItem* >(pState); + if (pBoolItem) + mxDspMasterObjects->set_active(pBoolItem->GetValue()); + } + break; + case SID_SELECT_BACKGROUND: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + Update(); + } + } + break; + case SID_ATTR_METRIC: + { + FieldUnit eFUnit = GetCurrentUnit(eState, pState); + if (meFUnit != eFUnit) + { + meFUnit = eFUnit; + SetMarginsFieldUnit(); + UpdateMarginBox(); + } + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, FillStyleModifyHdl, weld::ComboBox&, void) +{ + const eFillStyle nPos = static_cast<eFillStyle>(mxFillStyle->get_active()); + Update(); + + switch (nPos) + { + case NONE: + { + const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_FILLSTYLE, SfxCallMode::RECORD, { &aXFillStyleItem }); + } + break; + + case SOLID: + { + if (mpColorItem) + { + const XFillColorItem aItem( OUString(), mpColorItem->GetColorValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case GRADIENT: + { + if (mpGradientItem) + { + const XFillGradientItem aItem( mpGradientItem->GetName(), mpGradientItem->GetGradientValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case HATCH: + { + if (mpHatchItem) + { + const XFillHatchItem aItem( mpHatchItem->GetName(), mpHatchItem->GetHatchValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case BITMAP: + case PATTERN: + { + if (mpBitmapItem) + { + const XFillBitmapItem aItem( mpBitmapItem->GetName(), mpBitmapItem->GetGraphicObject() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + default: + break; + } +//TODO mxFillStyle->Selected(); +} + +IMPL_LINK_NOARG(SlideBackground, PaperSizeModifyHdl, weld::ComboBox&, void) +{ + const Paper ePaper = mxPaperSizeBox->get_active_id(); + Size aSize(SvxPaperInfo::GetPaperSize(ePaper, meUnit)); + + if (mxPaperOrientation->get_active() == 0) + Swap(aSize); + + mpPageItem->SetLandscape(mxPaperOrientation->get_active() == 0); + const SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, aSize); + // Page/slide properties dialog (FuPage::ExecuteDialog and ::ApplyItemSet) misuses + // SID_ATTR_PAGE_EXT1 to distinguish between Impress and Draw, as for whether to fit + // objects to paper size. Until that is handled somehow better, we do the same here + const SfxBoolItem aFitObjs(SID_ATTR_PAGE_EXT1, IsImpress()); + + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, SfxCallMode::RECORD, + { &aSizeItem, mpPageItem.get(), &aFitObjs }); + + // Notify LOK clients of the page size change. + if (!comphelper::LibreOfficeKit::isActive()) + return; + + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while (pViewShell) + { + if (pViewShell->GetDocId() == mrBase.GetDocId()) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel<SdXImpressDocument>(pViewShell->GetCurrentDocument()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } + pViewShell = SfxViewShell::GetNext(*pViewShell); + } +} + +IMPL_LINK_NOARG(SlideBackground, FillColorHdl, ColorListBox&, void) +{ + const drawing::FillStyle eXFS = static_cast<drawing::FillStyle>(mxFillStyle->get_active()); + switch(eXFS) + { + case drawing::FillStyle_SOLID: + { + XFillColorItem aItem(OUString(), mxFillLB->GetSelectEntryColor()); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + break; + case drawing::FillStyle_GRADIENT: + { + XGradient aGradient; + aGradient.SetStartColor(mxFillGrad1->GetSelectEntryColor()); + aGradient.SetEndColor(mxFillGrad2->GetSelectEntryColor()); + + // the name doesn't really matter, it'll be converted to unique one eventually, + // but it has to be non-empty + XFillGradientItem aItem("gradient", aGradient); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, FillBackgroundHdl, weld::ComboBox&, void) +{ + const eFillStyle nFillPos = static_cast<eFillStyle>(mxFillStyle->get_active()); + SfxObjectShell* pSh = SfxObjectShell::Current(); + if (!pSh) + return; + switch(nFillPos) + { + + case HATCH: + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + sal_uInt16 nPos = mxFillAttr->get_active(); + XHatch aHatch = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetHatch(); + const OUString aHatchName = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetName(); + + XFillHatchItem aItem(aHatchName, aHatch); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + break; + + case BITMAP: + case PATTERN: + { + sal_Int16 nPos = mxFillAttr->get_active(); + GraphicObject aBitmap; + OUString aName; + if( nFillPos == BITMAP ) + { + SvxBitmapListItem const * pBitmapListItem = pSh->GetItem(SID_BITMAP_LIST); + aBitmap = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetGraphicObject(); + aName = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetName(); + } + else if( nFillPos == PATTERN ) + { + SvxPatternListItem const * pPatternListItem = pSh->GetItem(SID_PATTERN_LIST); + aBitmap = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetGraphicObject(); + aName = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetName(); + } + XFillBitmapItem aItem(aName, aBitmap); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + break; + + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, AssignMasterPage, weld::ComboBox&, void) +{ + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell*>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = pDocSh ? pDocSh->GetDoc() : nullptr; + if (!pDoc) + return; + + auto pSSVS = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(mrBase); + if (pSSVS == nullptr) + return; + + auto& rSSController = pSSVS->GetSlideSorter().GetController(); + auto& rPageSelector = rSSController.GetPageSelector(); + + for( sal_uInt16 nPage = 0; nPage < pDoc->GetSdPageCount(PageKind::Standard); nPage++ ) + { + if (rPageSelector.IsPageSelected(nPage)) + { + OUString aLayoutName(mxMasterSlide->get_active_text()); + pDoc->SetMasterPage(nPage, aLayoutName, pDoc, false, false); + } + } +} + +IMPL_LINK_NOARG(SlideBackground, EditMasterHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_SLIDE_MASTER_MODE, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, SelectBgHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_SELECT_BACKGROUND, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, CloseMasterHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_CLOSE_MASTER_VIEW, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, DspBackground, weld::Toggleable&, void) +{ + bool IsChecked = mxDspMasterBackground->get_active(); + const SfxBoolItem aBoolItem(SID_DISPLAY_MASTER_BACKGROUND, IsChecked); + GetBindings()->GetDispatcher()->ExecuteList(SID_DISPLAY_MASTER_BACKGROUND, SfxCallMode::RECORD, { &aBoolItem }); +} + +IMPL_LINK_NOARG(SlideBackground, DspObjects, weld::Toggleable&, void) +{ + bool IsChecked = mxDspMasterObjects->get_active(); + const SfxBoolItem aBoolItem(SID_DISPLAY_MASTER_OBJECTS,IsChecked); + GetBindings()->GetDispatcher()->ExecuteList(SID_DISPLAY_MASTER_OBJECTS, SfxCallMode::RECORD, { &aBoolItem, &aBoolItem }); +} + +IMPL_LINK_NOARG( SlideBackground, ModifyMarginHdl, weld::ComboBox&, void ) +{ + bool bApplyNewPageMargins = true; + switch ( mxMarginSelectBox->get_active() ) + { + case 0: + SetNone(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 1: + SetNarrow(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 2: + SetModerate(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 3: + SetNormal075(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 4: + SetNormal100(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 5: + SetNormal125(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 6: + SetWide(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + default: + bApplyNewPageMargins = false; + break; + } + + if(bApplyNewPageMargins) + { + ExecuteMarginLRChange(m_nPageLeftMargin, m_nPageRightMargin); + ExecuteMarginULChange(m_nPageTopMargin, m_nPageBottomMargin); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/SlideBackground.hxx b/sd/source/ui/sidebar/SlideBackground.hxx new file mode 100644 index 000000000..25af0a4af --- /dev/null +++ b/sd/source/ui/sidebar/SlideBackground.hxx @@ -0,0 +1,180 @@ +/* -*- 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 . + */ + +#pragma once + +#include <memory> +#include <sfx2/sidebar/PanelLayout.hxx> +#include <sfx2/sidebar/ControllerItem.hxx> +#include <svx/papersizelistbox.hxx> +#include <sfx2/sidebar/IContextChangeReceiver.hxx> +#include <vcl/EnumContext.hxx> + +#include <com/sun/star/frame/XFrame.hpp> + +namespace sd { class ViewShellBase; } +namespace sd::tools { class EventMultiplexerEvent; } + +class ColorListBox; +class SvxPageItem; +class SvxLongLRSpaceItem; +class SvxLongULSpaceItem; +class XFillColorItem; +class XGradient; +class XFillGradientItem; +class XFillBitmapItem; +class XFillHatchItem; + +namespace sd::sidebar { + +class SlideBackground : + public PanelLayout, + public ::sfx2::sidebar::IContextChangeReceiver, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + SlideBackground( + weld::Widget* pParent, + ViewShellBase& rBase, + const css::uno::Reference<css::frame::XFrame>& rxFrame, + SfxBindings* pBindings ); + virtual ~SlideBackground() override; + SfxBindings* GetBindings() { return mpBindings; } + // Window + virtual void NotifyItemUpdate( + const sal_uInt16 nSID, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + virtual void HandleContextChange( + const vcl::EnumContext& rContext) override; + virtual void DumpAsPropertyTree(::tools::JsonWriter&) override; + +private: + + ViewShellBase& mrBase; + + std::unique_ptr<SvxPaperSizeListBox> mxPaperSizeBox; + std::unique_ptr<weld::ComboBox> mxPaperOrientation; + std::unique_ptr<weld::ComboBox> mxMasterSlide; + std::unique_ptr<weld::Label> mxBackgroundLabel; + std::unique_ptr<weld::ComboBox> mxFillStyle; + std::unique_ptr<ColorListBox> mxFillLB; + std::unique_ptr<weld::ComboBox> mxFillAttr; + std::unique_ptr<ColorListBox> mxFillGrad1; + std::unique_ptr<ColorListBox> mxFillGrad2; + std::unique_ptr<weld::Button> mxInsertImage; + std::unique_ptr<weld::CheckButton> mxDspMasterBackground; + std::unique_ptr<weld::CheckButton> mxDspMasterObjects; + std::unique_ptr<weld::Button> mxCloseMaster; + std::unique_ptr<weld::Button> mxEditMaster; + std::unique_ptr<weld::Label> mxMasterLabel; + std::unique_ptr<weld::ComboBox> mxMarginSelectBox; + std::unique_ptr<weld::Label> mxCustomEntry; + std::unique_ptr<weld::Label> mxMarginLabel; + + ::sfx2::sidebar::ControllerItem maPaperSizeController; + ::sfx2::sidebar::ControllerItem maPaperOrientationController; + ::sfx2::sidebar::ControllerItem maPaperMarginLRController; + ::sfx2::sidebar::ControllerItem maPaperMarginULController; + ::sfx2::sidebar::ControllerItem maBckColorController; + ::sfx2::sidebar::ControllerItem maBckGradientController; + ::sfx2::sidebar::ControllerItem maBckHatchController; + ::sfx2::sidebar::ControllerItem maBckBitmapController; + ::sfx2::sidebar::ControllerItem maBckFillStyleController; + ::sfx2::sidebar::ControllerItem maBckImageController; + ::sfx2::sidebar::ControllerItem maDspBckController; + ::sfx2::sidebar::ControllerItem maDspObjController; + ::sfx2::sidebar::ControllerItem maMetricController; + ::sfx2::sidebar::ControllerItem maCloseMasterController; + + std::unique_ptr< SvxPageItem > mpPageItem; + std::unique_ptr< XFillColorItem > mpColorItem; + std::unique_ptr< XFillGradientItem > mpGradientItem; + std::unique_ptr< XFillHatchItem > mpHatchItem; + std::unique_ptr< XFillBitmapItem > mpBitmapItem; + + bool mbSwitchModeToNormal; + bool mbSwitchModeToMaster; + + css::uno::Reference<css::frame::XFrame> mxFrame; + vcl::EnumContext maContext; + vcl::EnumContext maDrawOtherContext; + vcl::EnumContext maDrawMasterContext; + vcl::EnumContext maImpressOtherContext; + vcl::EnumContext maImpressMasterContext; + vcl::EnumContext maImpressHandoutContext; + vcl::EnumContext maImpressNotesContext; + bool mbTitle; + std::unique_ptr<SvxLongLRSpaceItem> mpPageLRMarginItem; + std::unique_ptr<SvxLongULSpaceItem> mpPageULMarginItem; + ::tools::Long m_nPageLeftMargin; + ::tools::Long m_nPageRightMargin; + ::tools::Long m_nPageTopMargin; + ::tools::Long m_nPageBottomMargin; + FieldUnit meFUnit; + OUString maCustomEntry; + + SfxBindings* mpBindings; + + MapUnit meUnit; + + DECL_LINK(FillBackgroundHdl, weld::ComboBox&, void); + DECL_LINK(FillStyleModifyHdl, weld::ComboBox&, void); + DECL_LINK(PaperSizeModifyHdl, weld::ComboBox&, void); + DECL_LINK(FillColorHdl, ColorListBox&, void); + DECL_LINK(AssignMasterPage, weld::ComboBox&, void); + DECL_LINK(DspBackground, weld::Toggleable&, void); + DECL_LINK(DspObjects, weld::Toggleable&, void); + DECL_LINK(CloseMasterHdl, weld::Button&, void); + DECL_LINK(EditMasterHdl, weld::Button&, void); + DECL_LINK(SelectBgHdl, weld::Button&, void); + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK( ModifyMarginHdl, weld::ComboBox&, void ); + + void Initialize(); + void Update(); + void UpdateMarginBox(); + void SetPanelTitle(const OUString& rTitle); + void SetMarginsFieldUnit(); + + Color const & GetColorSetOrDefault(); + XGradient const & GetGradientSetOrDefault(); + OUString const & GetHatchingSetOrDefault(); + OUString const & GetBitmapSetOrDefault(); + OUString const & GetPatternSetOrDefault(); + bool IsDraw(); + bool IsImpress(); + void addListener(); + void removeListener(); + void ExecuteMarginLRChange(const ::tools::Long mnPageLeftMargin, const ::tools::Long mnPageRightMargin); + void ExecuteMarginULChange(const ::tools::Long mnPageTopMargin, const ::tools::Long mnPageBottomMargin); + void populateMasterSlideDropdown(); + void updateMasterSlideSelection(); + + static FieldUnit GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |