diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sfx2/source/dialog/templdlg.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sfx2/source/dialog/templdlg.cxx')
-rw-r--r-- | sfx2/source/dialog/templdlg.cxx | 2334 |
1 files changed, 2334 insertions, 0 deletions
diff --git a/sfx2/source/dialog/templdlg.cxx b/sfx2/source/dialog/templdlg.cxx new file mode 100644 index 000000000..40851576f --- /dev/null +++ b/sfx2/source/dialog/templdlg.cxx @@ -0,0 +1,2334 @@ +/* -*- 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 <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <vcl/commandevent.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/event.hxx> +#include <vcl/help.hxx> +#include <vcl/menu.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/style.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <unotools/intlwrapper.hxx> +#include <unotools/collatorwrapper.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/UnknownModuleException.hpp> +#include <officecfg/Office/Common.hxx> + +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/templdlg.hxx> +#include <templdgi.hxx> +#include <tplcitem.hxx> +#include <sfx2/styfitem.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/newstyle.hxx> +#include <sfx2/tplpitem.hxx> +#include <sfx2/sfxresid.hxx> + +#include <sfx2/sfxsids.hrc> +#include <sfx2/strings.hrc> +#include <sfx2/docfac.hxx> +#include <sfx2/module.hxx> +#include <helpids.h> +#include <bitmaps.hlst> +#include <sfx2/viewfrm.hxx> + +#include <comphelper/string.hxx> + +#include <sfx2/StyleManager.hxx> +#include <sfx2/StylePreviewRenderer.hxx> + +using namespace css; +using namespace css::beans; +using namespace css::frame; +using namespace css::uno; + +class SfxCommonTemplateDialog_Impl::DeletionWatcher +{ + typedef void (DeletionWatcher::* bool_type)(); + +public: + explicit DeletionWatcher(SfxCommonTemplateDialog_Impl& rDialog) + : m_pDialog(&rDialog) + , m_pPrevious(m_pDialog->impl_setDeletionWatcher(this)) + { + } + + ~DeletionWatcher() + { + if (m_pDialog) + m_pDialog->impl_setDeletionWatcher(m_pPrevious); + } + + DeletionWatcher(const DeletionWatcher&) = delete; + DeletionWatcher& operator=(const DeletionWatcher&) = delete; + + // Signal that the dialog was deleted + void signal() + { + m_pDialog = nullptr; + if (m_pPrevious) + m_pPrevious->signal(); + } + + // Return true if the dialog was deleted + operator bool_type() const + { + return m_pDialog ? nullptr : &DeletionWatcher::signal; + } + +private: + SfxCommonTemplateDialog_Impl* m_pDialog; + DeletionWatcher *const m_pPrevious; /// let's add more epicycles! +}; + +/** Drop is enabled as long as it is allowed to create a new style by example, i.e. to + create a style out of the current selection. +*/ +sal_Int8 SfxCommonTemplateDialog_Impl::AcceptDrop(const AcceptDropEvent& rEvt, const DropTargetHelper& rHelper) +{ + if (rHelper.IsDropFormatSupported(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + // special case: page styles are allowed to create new styles by example + // but not allowed to be created by drag and drop + if (GetActualFamily() == SfxStyleFamily::Page || bNewByExampleDisabled) + return DND_ACTION_NONE; + else + return DND_ACTION_COPY; + } + + // to enable the autoscroll when we're close to the edges + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + pTreeView->get_dest_row_at_pos(rEvt.maPosPixel, nullptr); + return DND_ACTION_MOVE; +} + +sal_Int8 SfxCommonTemplateDialog_Impl::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + // handle drop of content into the treeview to create a new style + SfxObjectShell* pDocShell = GetObjectShell(); + if (pDocShell) + { + TransferableDataHelper aHelper(rEvt.maDropEvent.Transferable); + sal_uInt32 nFormatCount = aHelper.GetFormatCount(); + + sal_Int8 nRet = DND_ACTION_NONE; + + bool bFormatFound = false; + + for ( sal_uInt32 i = 0; i < nFormatCount; ++i ) + { + SotClipboardFormatId nId = aHelper.GetFormat(i); + TransferableObjectDescriptor aDesc; + + if ( aHelper.GetTransferableObjectDescriptor( nId, aDesc ) ) + { + if ( aDesc.maClassName == pDocShell->GetFactory().GetClassId() ) + { + Application::PostUserEvent(LINK(this, SfxCommonTemplateDialog_Impl, OnAsyncExecuteDrop)); + + bFormatFound = true; + nRet = rEvt.mnAction; + break; + } + } + } + + if (bFormatFound) + return nRet; + } + + if (!mxTreeBox->get_visible()) + return DND_ACTION_NONE; + + if (!bAllowReParentDrop) + return DND_ACTION_NONE; + + // otherwise if we're dragging with the treeview to set a new parent of the dragged style + weld::TreeView* pSource = mxTreeBox->get_drag_source(); + // only dragging within the same widget allowed + if (!pSource || pSource != mxTreeBox.get()) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xSource(mxTreeBox->make_iterator()); + if (!mxTreeBox->get_selected(xSource.get())) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xTarget(mxTreeBox->make_iterator()); + if (!mxTreeBox->get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get())) + { + // if nothing under the mouse, use the last row + int nChildren = mxTreeBox->n_children(); + if (!nChildren) + return DND_ACTION_NONE; + if (!mxTreeBox->get_iter_first(*xTarget) || !mxTreeBox->iter_nth_sibling(*xTarget, nChildren - 1)) + return DND_ACTION_NONE; + while (mxTreeBox->get_row_expanded(*xTarget)) + { + nChildren = mxTreeBox->iter_n_children(*xTarget); + if (!mxTreeBox->iter_children(*xTarget) || !mxTreeBox->iter_nth_sibling(*xTarget, nChildren - 1)) + return DND_ACTION_NONE; + } + } + OUString aTargetStyle = mxTreeBox->get_text(*xTarget); + DropHdl(mxTreeBox->get_text(*xSource), aTargetStyle); + mxTreeBox->unset_drag_dest_row(); + FillTreeBox(); + SelectStyle(aTargetStyle, false); + return DND_ACTION_NONE; +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + // Allow normal processing. only if bAllowReParentDrop is true + return !bAllowReParentDrop; +} + +IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, OnAsyncExecuteDrop, void*, void) +{ + ActionSelect("new"); +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool) +{ + bool bRet = false; + const vcl::KeyCode& rKeyCode = rKeyEvent.GetKeyCode(); + if (bCanDel && !rKeyCode.GetModifier() && rKeyCode.GetCode() == KEY_DELETE) + { + DeleteHdl(); + bRet = true; + } + return bRet; +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString) +{ + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + const OUString aTemplName(pTreeView->get_text(rEntry)); + OUString sQuickHelpText(aTemplName); + + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + if (!pItem) + return sQuickHelpText; + SfxStyleSheetBase* pStyle = pStyleSheetPool->Find(aTemplName, pItem->GetFamily()); + + if (pStyle && pStyle->IsUsed()) // pStyle is in use in the document? + { + OUString sUsedBy; + if (pStyle->GetFamily() == SfxStyleFamily::Pseudo) + sUsedBy = pStyle->GetUsedBy(); + + if (!sUsedBy.isEmpty()) + { + const sal_Int32 nMaxLen = 80; + if (sUsedBy.getLength() > nMaxLen) + { + sUsedBy = sUsedBy.copy(0, nMaxLen) + "..."; + } + + OUString aMessage = SfxResId(STR_STYLEUSEDBY); + aMessage = aMessage.replaceFirst("%STYLELIST", sUsedBy); + sQuickHelpText = aTemplName + " " + aMessage; + } + } + + return sQuickHelpText; +} + +IMPL_STATIC_LINK(SfxCommonTemplateDialog_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size) +{ + vcl::RenderContext& rRenderContext = aPayload.first; + return Size(42, 32 * rRenderContext.GetDPIScaleFactor()); +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void) +{ + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + ::tools::Rectangle aRect(rRect.TopLeft(), Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight())); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + rRenderContext.Push(PushFlags::TEXTCOLOR); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + + bool bSuccess = false; + + SfxObjectShell* pShell = SfxObjectShell::Current(); + sfx2::StyleManager* pStyleManager = pShell ? pShell->GetStyleManager(): nullptr; + + if (pStyleManager) + { + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + SfxStyleSheetBase* pStyleSheet = pStyleManager->Search(rId, pItem->GetFamily()); + + if (pStyleSheet) + { + rRenderContext.Push(PushFlags::ALL); + sal_Int32 nSize = aRect.GetHeight(); + std::unique_ptr<sfx2::StylePreviewRenderer> pStylePreviewRenderer( + pStyleManager->CreateStylePreviewRenderer(rRenderContext, pStyleSheet, nSize)); + bSuccess = pStylePreviewRenderer->recalculate() && pStylePreviewRenderer->render(aRect); + rRenderContext.Pop(); + } + } + + if (!bSuccess) + rRenderContext.DrawText(aRect, rId, DrawTextFlags::Left | DrawTextFlags::VCenter); + + rRenderContext.Pop(); +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, PopupFlatMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + PrepareMenu(rCEvt.GetMousePosPixel()); + + if (mxFmtLb->count_selected_rows() <= 0) + { + EnableEdit(false); + EnableDel(false); + } + + ShowMenu(rCEvt); + + return true; +} + +void SfxCommonTemplateDialog_Impl::PrepareMenu(const Point& rPos) +{ + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + std::unique_ptr<weld::TreeIter> xIter(pTreeView->make_iterator()); + if (pTreeView->get_dest_row_at_pos(rPos, xIter.get(), false) && !pTreeView->is_selected(*xIter)) + { + pTreeView->unselect_all(); + pTreeView->set_cursor(*xIter); + pTreeView->select(*xIter); + FmtSelectHdl(*pTreeView); + } +} + +void SfxCommonTemplateDialog_Impl::ShowMenu(const CommandEvent& rCEvt) +{ + CreateContextMenu(); + + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + OString sCommand(mxMenu->popup_at_rect(pTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); + MenuSelect(sCommand); +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, PopupTreeMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + PrepareMenu(rCEvt.GetMousePosPixel()); + + ShowMenu(rCEvt); + + return true; +} + +SfxTemplatePanelControl::SfxTemplatePanelControl(SfxBindings* pBindings, vcl::Window* pParentWindow) + : PanelLayout(pParentWindow, "TemplatePanel", "sfx/ui/templatepanel.ui", nullptr) + , pImpl(new SfxTemplateDialog_Impl(pBindings, this)) +{ + OSL_ASSERT(pBindings!=nullptr); +} + +SfxTemplatePanelControl::~SfxTemplatePanelControl() +{ + disposeOnce(); +} + +void SfxTemplatePanelControl::dispose() +{ + pImpl.reset(); + PanelLayout::dispose(); +} + +static void MakeExpanded_Impl(weld::TreeView& rBox, std::vector<OUString>& rEntries) +{ + std::unique_ptr<weld::TreeIter> xEntry = rBox.make_iterator(); + if (rBox.get_iter_first(*xEntry)) + { + do + { + if (rBox.get_row_expanded(*xEntry)) + rEntries.push_back(rBox.get_text(*xEntry)); + } while (rBox.iter_next(*xEntry)); + } +} + +/** Internal structure for the establishment of the hierarchical view */ +namespace { + +class StyleTree_Impl; + +} + +typedef std::vector<std::unique_ptr<StyleTree_Impl>> StyleTreeArr_Impl; + +namespace { + +class StyleTree_Impl +{ +private: + OUString aName; + OUString aParent; + StyleTreeArr_Impl pChildren; + +public: + bool HasParent() const { return !aParent.isEmpty(); } + + StyleTree_Impl(const OUString &rName, const OUString &rParent): + aName(rName), aParent(rParent), pChildren(0) {} + + const OUString& getName() const { return aName; } + const OUString& getParent() const { return aParent; } + StyleTreeArr_Impl& getChildren() { return pChildren; } +}; + +} + +static void MakeTree_Impl(StyleTreeArr_Impl& rArr, const OUString& aUIName) +{ + const comphelper::string::NaturalStringSorter aSorter( + ::comphelper::getProcessComponentContext(), + Application::GetSettings().GetLanguageTag().getLocale()); + + std::unordered_map<OUString, StyleTree_Impl*> styleFinder; + styleFinder.reserve(rArr.size()); + for (const auto& pEntry : rArr) + { + styleFinder.emplace(pEntry->getName(), pEntry.get()); + } + + // Arrange all under their Parents + for (auto& pEntry : rArr) + { + if (!pEntry->HasParent()) + continue; + auto it = styleFinder.find(pEntry->getParent()); + if (it != styleFinder.end()) + { + StyleTree_Impl* pCmp = it->second; + // Insert child entries sorted + auto iPos = std::lower_bound(pCmp->getChildren().begin(), pCmp->getChildren().end(), pEntry, + [&aSorter](std::unique_ptr<StyleTree_Impl> const & pEntry1, std::unique_ptr<StyleTree_Impl> const & pEntry2) { return aSorter.compare(pEntry1->getName(), pEntry2->getName()) < 0; }); + pCmp->getChildren().insert(iPos, std::move(pEntry)); + } + } + + // Only keep tree roots in rArr, child elements can be accessed through the hierarchy + rArr.erase(std::remove_if(rArr.begin(), rArr.end(), [](std::unique_ptr<StyleTree_Impl> const & pEntry) { return !pEntry; }), rArr.end()); + + // tdf#91106 sort top level styles + std::sort(rArr.begin(), rArr.end()); + std::sort(rArr.begin(), rArr.end(), + [&aSorter, &aUIName](std::unique_ptr<StyleTree_Impl> const & pEntry1, std::unique_ptr<StyleTree_Impl> const & pEntry2) { + if (pEntry2->getName() == aUIName) + return false; + if (pEntry1->getName() == aUIName) + return true; // default always first + return aSorter.compare(pEntry1->getName(), pEntry2->getName()) < 0; + }); +} + +static bool IsExpanded_Impl( const std::vector<OUString>& rEntries, + const OUString &rStr) +{ + for (const auto & rEntry : rEntries) + { + if (rEntry == rStr) + return true; + } + return false; +} + +static void FillBox_Impl(weld::TreeView& rBox, + StyleTree_Impl* pEntry, + const std::vector<OUString>& rEntries, + SfxStyleFamily eStyleFamily, + weld::TreeIter* pParent) +{ + std::unique_ptr<weld::TreeIter> xResult = rBox.make_iterator(); + const OUString& rName = pEntry->getName(); + rBox.insert(pParent, -1, &rName, &rName, nullptr, nullptr, nullptr, false, xResult.get()); + + for (size_t i = 0; i < pEntry->getChildren().size(); ++i) + FillBox_Impl(rBox, pEntry->getChildren()[i].get(), rEntries, eStyleFamily, xResult.get()); +} + +namespace SfxTemplate +{ + // converts from SFX_STYLE_FAMILY Ids to 1-6 + static sal_uInt16 SfxFamilyIdToNId(SfxStyleFamily nFamily) + { + switch ( nFamily ) + { + case SfxStyleFamily::Char: return 1; + case SfxStyleFamily::Para: return 2; + case SfxStyleFamily::Frame: return 3; + case SfxStyleFamily::Page: return 4; + case SfxStyleFamily::Pseudo: return 5; + case SfxStyleFamily::Table: return 6; + default: return 0xffff; + } + } + + // converts from 1-6 to SFX_STYLE_FAMILY Ids + static SfxStyleFamily NIdToSfxFamilyId(sal_uInt16 nId) + { + switch (nId) + { + case 1: return SfxStyleFamily::Char; + case 2: return SfxStyleFamily::Para; + case 3: return SfxStyleFamily::Frame; + case 4: return SfxStyleFamily::Page; + case 5: return SfxStyleFamily::Pseudo; + case 6: return SfxStyleFamily::Table; + default: return SfxStyleFamily::All; + } + } +} + +// Constructor + +SfxCommonTemplateDialog_Impl::SfxCommonTemplateDialog_Impl(SfxBindings* pB, vcl::Window* pW, weld::Builder* pBuilder) + : pBindings(pB) + , pWindow(pW) + , pModule(nullptr) + , pStyleSheetPool(nullptr) + , pCurObjShell(nullptr) + , xModuleManager(frame::ModuleManager::create(::comphelper::getProcessComponentContext())) + , m_pDeletionWatcher(nullptr) + , mxFmtLb(pBuilder->weld_tree_view("flatview")) + , mxTreeBox(pBuilder->weld_tree_view("treeview")) + , mxPreviewCheckbox(pBuilder->weld_check_button("showpreview")) + , mxFilterLb(pBuilder->weld_combo_box("filter")) + + , nActFamily(0xffff) + , nActFilter(0) + , nAppFilter(SfxStyleSearchBits::Auto) + + , m_nModifier(0) + , bDontUpdate(false) + , bIsWater(false) + , bUpdate(false) + , bUpdateFamily(false) + , bCanEdit(false) + , bCanDel(false) + , bCanNew(true) + , bCanHide(true) + , bCanShow(false) + , bWaterDisabled(false) + , bNewByExampleDisabled(false) + , bUpdateByExampleDisabled(false) + , bTreeDrag(true) + , bAllowReParentDrop(false) + , bHierarchical(false) + , m_bWantHierarchical(false) + , bBindingUpdate(true) +{ + mxFmtLb->set_help_id(HID_TEMPLATE_FMT); + mxFilterLb->set_help_id(HID_TEMPLATE_FILTER); + mxPreviewCheckbox->set_active(officecfg::Office::Common::StylesAndFormatting::Preview::get()); +} + +sal_uInt16 SfxCommonTemplateDialog_Impl::StyleNrToInfoOffset(sal_uInt16 nId) +{ + const SfxStyleFamilyItem& rItem = pStyleFamilies->at( nId ); + return SfxTemplate::SfxFamilyIdToNId(rItem.GetFamily())-1; +} + +void SfxTemplateDialog_Impl::EnableEdit(bool bEnable) +{ + SfxCommonTemplateDialog_Impl::EnableEdit( bEnable ); + if( !bEnable || !bUpdateByExampleDisabled ) + EnableItem("update", bEnable); +} + +void SfxCommonTemplateDialog_Impl::ReadResource() +{ + // Read global user resource + for (auto & i : pFamilyState) + i.reset(); + + SfxViewFrame* pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame(); + pCurObjShell = pViewFrame->GetObjectShell(); + pModule = pCurObjShell ? pCurObjShell->GetModule() : nullptr; + if (pModule) + pStyleFamilies = pModule->CreateStyleFamilies(); + if (!pStyleFamilies) + pStyleFamilies.reset(new SfxStyleFamilies); + + nActFilter = 0xffff; + if (pCurObjShell) + { + nActFilter = static_cast< sal_uInt16 >( LoadFactoryStyleFilter( pCurObjShell ) ); + if ( 0xffff == nActFilter ) + nActFilter = pCurObjShell->GetAutoStyleFilterIndex(); + } + + // Paste in the toolbox + // reverse order, since always inserted at the head + size_t nCount = pStyleFamilies->size(); + + pBindings->ENTERREGISTRATIONS(); + + size_t i; + for (i = 0; i < nCount; ++i) + { + sal_uInt16 nSlot = 0; + switch (pStyleFamilies->at(i).GetFamily()) + { + case SfxStyleFamily::Char: + nSlot = SID_STYLE_FAMILY1; break; + case SfxStyleFamily::Para: + nSlot = SID_STYLE_FAMILY2; break; + case SfxStyleFamily::Frame: + nSlot = SID_STYLE_FAMILY3; break; + case SfxStyleFamily::Page: + nSlot = SID_STYLE_FAMILY4; break; + case SfxStyleFamily::Pseudo: + nSlot = SID_STYLE_FAMILY5; break; + case SfxStyleFamily::Table: + nSlot = SID_STYLE_FAMILY6; break; + default: OSL_FAIL("unknown StyleFamily"); break; + } + pBoundItems[i].reset( + new SfxTemplateControllerItem(nSlot, *this, *pBindings) ); + } + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_WATERCAN, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_NEW_BY_EXAMPLE, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_UPDATE_BY_EXAMPLE, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_NEW, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_DRAGHIERARCHIE, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_EDIT, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_DELETE, *this, *pBindings) ); + pBoundItems[i++].reset( new SfxTemplateControllerItem( + SID_STYLE_FAMILY, *this, *pBindings) ); + pBindings->LEAVEREGISTRATIONS(); + + for(; i < COUNT_BOUND_FUNC; ++i) + pBoundItems[i] = nullptr; + + StartListening(*pBindings); + +// Insert in the reverse order of occurrence in the Style Families. This is for +// the toolbar of the designer. The list box of the catalog respects the +// correct order by itself. + +// Sequences: the order of Resource = the order of Toolbar for example list box. +// Order of ascending SIDs: Low SIDs are displayed first when templates of +// several families are active. + + // in the Writer the UpdateStyleByExample Toolbox button is removed and + // the NewStyle button gets a PopupMenu + if(nCount > 4) + ReplaceUpdateButtonByMenu(); + + for( ; nCount--; ) + { + const SfxStyleFamilyItem &rItem = pStyleFamilies->at( nCount ); + sal_uInt16 nId = SfxTemplate::SfxFamilyIdToNId( rItem.GetFamily() ); + InsertFamilyItem(nId, rItem); + } + + for ( i = SID_STYLE_FAMILY1; i <= SID_STYLE_FAMILY4; i++ ) + pBindings->Update(i); +} + +void SfxCommonTemplateDialog_Impl::ClearResource() +{ + ClearFamilyList(); + impl_clear(); +} + +void SfxCommonTemplateDialog_Impl::impl_clear() +{ + pStyleFamilies.reset(); + for (auto & i : pFamilyState) + i.reset(); + for (auto & i : pBoundItems) + i.reset(); + pCurObjShell = nullptr; +} + +SfxCommonTemplateDialog_Impl::DeletionWatcher * +SfxCommonTemplateDialog_Impl::impl_setDeletionWatcher( + DeletionWatcher *const pNewWatcher) +{ + DeletionWatcher *const pRet(m_pDeletionWatcher); + m_pDeletionWatcher = pNewWatcher; + return pRet; +} + +class TreeViewDropTarget final : public DropTargetHelper +{ +private: + SfxCommonTemplateDialog_Impl& m_rParent; + +public: + TreeViewDropTarget(SfxCommonTemplateDialog_Impl& rDialog, weld::TreeView& rTreeView) + : DropTargetHelper(rTreeView.get_drop_target()) + , m_rParent(rDialog) + { + } + + virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override + { + return m_rParent.AcceptDrop(rEvt, *this); + } + + virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override + { + return m_rParent.ExecuteDrop(rEvt); + } +}; + +void SfxCommonTemplateDialog_Impl::Initialize() +{ + // Read global user resource + ReadResource(); + pBindings->Invalidate( SID_STYLE_FAMILY ); + pBindings->Update( SID_STYLE_FAMILY ); + + mxFilterLb->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FilterSelectHdl)); + mxFmtLb->connect_row_activated(LINK( this, SfxCommonTemplateDialog_Impl, TreeListApplyHdl)); + mxFmtLb->connect_mouse_press(LINK(this, SfxCommonTemplateDialog_Impl, MousePressHdl)); + mxFmtLb->connect_query_tooltip(LINK(this, SfxCommonTemplateDialog_Impl, QueryTooltipHdl)); + mxFmtLb->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FmtSelectHdl)); + mxFmtLb->connect_popup_menu(LINK(this, SfxCommonTemplateDialog_Impl, PopupFlatMenuHdl)); + mxFmtLb->connect_key_press(LINK(this, SfxCommonTemplateDialog_Impl, KeyInputHdl)); + mxFmtLb->set_selection_mode(SelectionMode::Multiple); + mxTreeBox->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FmtSelectHdl)); + mxTreeBox->connect_row_activated(LINK( this, SfxCommonTemplateDialog_Impl, TreeListApplyHdl)); + mxTreeBox->connect_mouse_press(LINK(this, SfxCommonTemplateDialog_Impl, MousePressHdl)); + mxTreeBox->connect_query_tooltip(LINK(this, SfxCommonTemplateDialog_Impl, QueryTooltipHdl)); + mxTreeBox->connect_popup_menu(LINK(this, SfxCommonTemplateDialog_Impl, PopupTreeMenuHdl)); + mxTreeBox->connect_key_press(LINK(this, SfxCommonTemplateDialog_Impl, KeyInputHdl)); + mxTreeBox->connect_drag_begin(LINK(this, SfxCommonTemplateDialog_Impl, DragBeginHdl)); + mxPreviewCheckbox->connect_clicked(LINK(this, SfxCommonTemplateDialog_Impl, PreviewHdl)); + m_xTreeView1DropTargetHelper.reset(new TreeViewDropTarget(*this, *mxFmtLb)); + m_xTreeView2DropTargetHelper.reset(new TreeViewDropTarget(*this, *mxTreeBox)); + + int nTreeHeight = mxFmtLb->get_height_rows(8); + mxFmtLb->set_size_request(-1, nTreeHeight); + mxTreeBox->set_size_request(-1, nTreeHeight); + + mxFmtLb->connect_custom_get_size(LINK(this, SfxCommonTemplateDialog_Impl, CustomGetSizeHdl)); + mxFmtLb->connect_custom_render(LINK(this, SfxCommonTemplateDialog_Impl, CustomRenderHdl)); + mxTreeBox->connect_custom_get_size(LINK(this, SfxCommonTemplateDialog_Impl, CustomGetSizeHdl)); + mxTreeBox->connect_custom_render(LINK(this, SfxCommonTemplateDialog_Impl, CustomRenderHdl)); + bool bCustomPreview = officecfg::Office::Common::StylesAndFormatting::Preview::get(); + mxFmtLb->set_column_custom_renderer(0, bCustomPreview); + mxTreeBox->set_column_custom_renderer(0, bCustomPreview); + + mxFmtLb->set_visible(!bHierarchical); + mxTreeBox->set_visible(bHierarchical); + + Update_Impl(); +} + +SfxCommonTemplateDialog_Impl::~SfxCommonTemplateDialog_Impl() +{ + if ( bIsWater ) + Execute_Impl(SID_STYLE_WATERCAN, "", "", 0); + GetWindow()->Hide(); + impl_clear(); + if ( pStyleSheetPool ) + EndListening(*pStyleSheetPool); + pStyleSheetPool = nullptr; + m_xTreeView1DropTargetHelper.reset(); + m_xTreeView2DropTargetHelper.reset(); + mxTreeBox.reset(); + pIdle.reset(); + if ( m_pDeletionWatcher ) + m_pDeletionWatcher->signal(); + mxFmtLb.reset(); + mxPreviewCheckbox.reset(); + mxFilterLb.reset(); +} + +// Helper function: Access to the current family item +const SfxStyleFamilyItem *SfxCommonTemplateDialog_Impl::GetFamilyItem_Impl() const +{ + const size_t nCount = pStyleFamilies->size(); + for(size_t i = 0; i < nCount; ++i) + { + const SfxStyleFamilyItem &rItem = pStyleFamilies->at( i ); + sal_uInt16 nId = SfxTemplate::SfxFamilyIdToNId(rItem.GetFamily()); + if(nId == nActFamily) + return &rItem; + } + return nullptr; +} + +void SfxCommonTemplateDialog_Impl::GetSelectedStyle() const +{ + if (!IsInitialized() || !pStyleSheetPool || !HasSelectedStyle()) + return; + const OUString aTemplName( GetSelectedEntry() ); + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + pStyleSheetPool->Find( aTemplName, pItem->GetFamily() ); +} + +/** + * Is it safe to show the water-can / fill icon. If we've a + * hierarchical widget - we have only single select, otherwise + * we need to check if we have a multi-selection. We either have + * a mxTreeBox showing or an mxFmtLb (which we hide when not shown) + */ +bool SfxCommonTemplateDialog_Impl::IsSafeForWaterCan() const +{ + if (mxTreeBox->get_visible()) + return mxTreeBox->get_selected_index() != -1; + else + return mxFmtLb->count_selected_rows() == 1; +} + +void SfxCommonTemplateDialog_Impl::SelectStyle(const OUString &rStr, bool bIsCallback) +{ + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + if ( !pItem ) + return; + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSheetBase* pStyle = pStyleSheetPool->Find( rStr, eFam ); + if( pStyle ) + { + bool bReadWrite = !(pStyle->GetMask() & SfxStyleSearchBits::ReadOnly); + EnableEdit( bReadWrite ); + EnableHide( bReadWrite && !pStyle->IsHidden( ) && !pStyle->IsUsed( ) ); + EnableShow( bReadWrite && pStyle->IsHidden( ) ); + } + else + { + EnableEdit(false); + EnableHide(false); + EnableShow(false); + } + + if (!bIsCallback) + { + if (mxTreeBox->get_visible()) + { + if (!rStr.isEmpty()) + { + std::unique_ptr<weld::TreeIter> xEntry = mxTreeBox->make_iterator(); + bool bEntry = mxTreeBox->get_iter_first(*xEntry); + while (bEntry) + { + if (mxTreeBox->get_text(*xEntry) == rStr) + { + mxTreeBox->scroll_to_row(*xEntry); + mxTreeBox->select(*xEntry); + break; + } + bEntry = mxTreeBox->iter_next(*xEntry); + } + } + else + mxTreeBox->unselect_all(); + } + else + { + bool bSelect = !rStr.isEmpty(); + if (bSelect) + { + std::unique_ptr<weld::TreeIter> xEntry = mxFmtLb->make_iterator(); + bool bEntry = mxFmtLb->get_iter_first(*xEntry); + while (bEntry && mxFmtLb->get_text(*xEntry) != rStr) + bEntry = mxFmtLb->iter_next(*xEntry); + if (!bEntry) + bSelect = false; + else + { + if (!mxFmtLb->is_selected(*xEntry)) + { + mxFmtLb->unselect_all(); + mxFmtLb->scroll_to_row(*xEntry); + mxFmtLb->select(*xEntry); + } + } + } + + if (!bSelect) + { + mxFmtLb->unselect_all(); + EnableEdit(false); + EnableHide(false); + EnableShow(false); + } + } + } + + bWaterDisabled = !IsSafeForWaterCan(); + + if (!bIsCallback) + { + // tdf#134598 call FmtSelect to update watercan + FmtSelect(nullptr, false); + } +} + +OUString SfxCommonTemplateDialog_Impl::GetSelectedEntry() const +{ + OUString aRet; + if (mxTreeBox->get_visible()) + aRet = mxTreeBox->get_selected_text(); + else + aRet = mxFmtLb->get_selected_text(); + return aRet; +} + +void SfxCommonTemplateDialog_Impl::EnableTreeDrag(bool bEnable) +{ + if (pStyleSheetPool) + { + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + SfxStyleSheetBase* pStyle = pItem ? pStyleSheetPool->First(pItem->GetFamily()) : nullptr; + bAllowReParentDrop = pStyle && pStyle->HasParentSupport() && bEnable; + } + bTreeDrag = bEnable; +} + +static OUString lcl_GetStyleFamilyName( SfxStyleFamily nFamily ) +{ + if(nFamily == SfxStyleFamily::Char) + return "CharacterStyles" ; + if(nFamily == SfxStyleFamily::Para) + return "ParagraphStyles"; + if(nFamily == SfxStyleFamily::Page) + return "PageStyles"; + if(nFamily == SfxStyleFamily::Table) + return "TableStyles"; + return OUString(); +} + +OUString SfxCommonTemplateDialog_Impl::getDefaultStyleName( const SfxStyleFamily eFam ) +{ + OUString sDefaultStyle; + OUString aFamilyName = lcl_GetStyleFamilyName(eFam); + if( aFamilyName == "TableStyles" ) + sDefaultStyle = "Default Style"; + else + sDefaultStyle = "Standard"; + uno::Reference< style::XStyleFamiliesSupplier > xModel(GetObjectShell()->GetModel(), uno::UNO_QUERY); + OUString aUIName; + try + { + uno::Reference< container::XNameAccess > xStyles; + uno::Reference< container::XNameAccess > xCont = xModel->getStyleFamilies(); + xCont->getByName( aFamilyName ) >>= xStyles; + uno::Reference< beans::XPropertySet > xInfo; + xStyles->getByName( sDefaultStyle ) >>= xInfo; + xInfo->getPropertyValue("DisplayName") >>= aUIName; + } + catch (const uno::Exception&) + { + } + return aUIName; +} + +void SfxCommonTemplateDialog_Impl::FillTreeBox() +{ + assert(mxTreeBox && "FillTreeBox() without treebox"); + if (!pStyleSheetPool || nActFamily == 0xffff) + return; + + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + if (!pItem) + return; + const SfxStyleFamily eFam = pItem->GetFamily(); + StyleTreeArr_Impl aArr; + SfxStyleSheetBase* pStyle = pStyleSheetPool->First(eFam, SfxStyleSearchBits::AllVisible); + + bAllowReParentDrop = pStyle && pStyle->HasParentSupport() && bTreeDrag; + + while (pStyle) + { + StyleTree_Impl* pNew = new StyleTree_Impl(pStyle->GetName(), pStyle->GetParent()); + aArr.emplace_back(pNew); + pStyle = pStyleSheetPool->Next(); + } + OUString aUIName = getDefaultStyleName(eFam); + MakeTree_Impl(aArr, aUIName); + std::vector<OUString> aEntries; + MakeExpanded_Impl(*mxTreeBox, aEntries); + mxTreeBox->freeze(); + mxTreeBox->clear(); + const sal_uInt16 nCount = aArr.size(); + + for (sal_uInt16 i = 0; i < nCount; ++i) + { + FillBox_Impl(*mxTreeBox, aArr[i].get(), aEntries, eFam, nullptr); + aArr[i].reset(); + } + + EnableItem("watercan", false); + + SfxTemplateItem* pState = pFamilyState[nActFamily - 1].get(); + + mxTreeBox->thaw(); + + std::unique_ptr<weld::TreeIter> xEntry = mxTreeBox->make_iterator(); + bool bEntry = mxTreeBox->get_iter_first(*xEntry); + if (bEntry && nCount) + mxTreeBox->expand_row(*xEntry); + + while (bEntry) + { + if (IsExpanded_Impl(aEntries, mxTreeBox->get_text(*xEntry))) + mxTreeBox->expand_row(*xEntry); + bEntry = mxTreeBox->iter_next(*xEntry); + } + + OUString aStyle; + if(pState) // Select current entry + aStyle = pState->GetStyleName(); + SelectStyle(aStyle, false); + EnableDelete(); +} + +bool SfxCommonTemplateDialog_Impl::HasSelectedStyle() const +{ + return mxTreeBox->get_visible() ? mxTreeBox->get_selected_index() != -1 + : mxFmtLb->count_selected_rows() != 0; +} + +// internal: Refresh the display +// nFlags: what we should update. +void SfxCommonTemplateDialog_Impl::UpdateStyles_Impl(StyleFlags nFlags) +{ + OSL_ENSURE(nFlags != StyleFlags::NONE, "nothing to do"); + const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl(); + if (!pItem) + { + // Is the case for the template catalog + const size_t nFamilyCount = pStyleFamilies->size(); + size_t n; + for( n = 0; n < nFamilyCount; n++ ) + if( pFamilyState[ StyleNrToInfoOffset(n) ] ) break; + if ( n == nFamilyCount ) + // It happens sometimes, God knows why + return; + nAppFilter = pFamilyState[StyleNrToInfoOffset(n)]->GetValue(); + FamilySelect( StyleNrToInfoOffset(n)+1 ); + pItem = GetFamilyItem_Impl(); + } + + const SfxStyleFamily eFam = pItem->GetFamily(); + + SfxStyleSearchBits nFilter (nActFilter < pItem->GetFilterList().size() ? pItem->GetFilterList()[nActFilter].nFlags : SfxStyleSearchBits::Auto); + if (nFilter == SfxStyleSearchBits::Auto) // automatic + nFilter = nAppFilter; + + OSL_ENSURE(pStyleSheetPool, "no StyleSheetPool"); + if(!pStyleSheetPool) + return; + + pItem = GetFamilyItem_Impl(); + if(nFlags & StyleFlags::UpdateFamily) // Update view type list (Hierarchical, All, etc. + { + CheckItem(OString::number(nActFamily)); // check Button in Toolbox + + mxFilterLb->freeze(); + mxFilterLb->clear(); + + //insert hierarchical at the beginning + mxFilterLb->append(OUString::number(static_cast<int>(SfxStyleSearchBits::All)), SfxResId(STR_STYLE_FILTER_HIERARCHICAL)); + const SfxStyleFilter& rFilter = pItem->GetFilterList(); + for (const SfxFilterTuple& i : rFilter) + mxFilterLb->append(OUString::number(static_cast<int>(i.nFlags)), i.aName); + mxFilterLb->thaw(); + + if (nActFilter < mxFilterLb->get_count() - 1) + mxFilterLb->set_active(nActFilter + 1); + else + { + nActFilter = 0; + mxFilterLb->set_active(1); + nFilter = (nActFilter < rFilter.size()) ? rFilter[nActFilter].nFlags : SfxStyleSearchBits::Auto; + } + + // if the tree view again, select family hierarchy + if (mxTreeBox->get_visible() || m_bWantHierarchical) + { + mxFilterLb->set_active_text(SfxResId(STR_STYLE_FILTER_HIERARCHICAL)); + EnableHierarchical(true); + } + } + else + { + if (nActFilter < mxFilterLb->get_count() - 1) + mxFilterLb->set_active(nActFilter + 1); + else + { + nActFilter = 0; + mxFilterLb->set_active(1); + } + } + + if(!(nFlags & StyleFlags::UpdateFamilyList)) + return; + + EnableItem("watercan", false); + + SfxStyleSheetBase *pStyle = pStyleSheetPool->First(eFam, nFilter); + + std::unique_ptr<weld::TreeIter> xEntry = mxFmtLb->make_iterator(); + bool bEntry = mxFmtLb->get_iter_first(*xEntry); + std::vector<OUString> aStrings; + + comphelper::string::NaturalStringSorter aSorter( + ::comphelper::getProcessComponentContext(), + Application::GetSettings().GetLanguageTag().getLocale()); + + while( pStyle ) + { + aStrings.push_back(pStyle->GetName()); + pStyle = pStyleSheetPool->Next(); + } + OUString aUIName = getDefaultStyleName(eFam); + + // Paradoxically, with a list and non-Latin style names, + // sorting twice is faster than sorting once. + // The first sort has a cheap comparator, and gets the list into mostly-sorted order. + // Then the second sort needs to call its (much more expensive) comparator less often. + std::sort(aStrings.begin(), aStrings.end()); + std::sort(aStrings.begin(), aStrings.end(), + [&aSorter, &aUIName](const OUString& rLHS, const OUString& rRHS) { + if(rRHS == aUIName) + return false; + if(rLHS == aUIName) + return true; // default always first + return aSorter.compare(rLHS, rRHS) < 0; + }); + + size_t nCount = aStrings.size(); + size_t nPos = 0; + while (nPos < nCount && bEntry && + aStrings[nPos] == mxFmtLb->get_text(*xEntry)) + { + ++nPos; + bEntry = mxFmtLb->iter_next(*xEntry); + } + + if (nPos < nCount || bEntry) + { + // Fills the display box + mxFmtLb->freeze(); + mxFmtLb->clear(); + + for (nPos = 0; nPos < nCount; ++nPos) + mxFmtLb->append(aStrings[nPos], aStrings[nPos]); + + mxFmtLb->thaw(); + } + // Selects the current style if any + SfxTemplateItem *pState = pFamilyState[nActFamily-1].get(); + OUString aStyle; + if(pState) + aStyle = pState->GetStyleName(); + SelectStyle(aStyle, false); + EnableDelete(); +} + +// Updated display: Watering the house +void SfxCommonTemplateDialog_Impl::SetWaterCanState(const SfxBoolItem *pItem) +{ + bWaterDisabled = (pItem == nullptr); + + if(!bWaterDisabled) + //make sure the watercan is only activated when there is (only) one selection + bWaterDisabled = !IsSafeForWaterCan(); + + if(pItem && !bWaterDisabled) + { + CheckItem("watercan", pItem->GetValue()); + EnableItem("watercan"); + } + else + { + if(!bWaterDisabled) + EnableItem("watercan"); + else + EnableItem("watercan", false); + } + +// Ignore while in watercan mode statusupdates + + size_t nCount = pStyleFamilies->size(); + pBindings->EnterRegistrations(); + for(size_t n = 0; n < nCount; n++) + { + SfxControllerItem *pCItem=pBoundItems[n].get(); + bool bChecked = pItem && pItem->GetValue(); + if( pCItem->IsBound() == bChecked ) + { + if( !bChecked ) + pCItem->ReBind(); + else + pCItem->UnBind(); + } + } + pBindings->LeaveRegistrations(); +} + +// Item with the status of a Family is copied and noted +// (is updated when all states have also been updated.) +// See also: <SfxBindings::AddDoneHdl(const Link &)> +void SfxCommonTemplateDialog_Impl::SetFamilyState( sal_uInt16 nSlotId, const SfxTemplateItem* pItem ) +{ + sal_uInt16 nIdx = nSlotId - SID_STYLE_FAMILY_START; + pFamilyState[nIdx].reset(); + if ( pItem ) + pFamilyState[nIdx].reset( new SfxTemplateItem(*pItem) ); + bUpdate = true; + + // If used templates (how the hell you find this out??) + bUpdateFamily = true; +} + +// Notice from SfxBindings that the update is completed. Pushes out the update +// of the display. +void SfxCommonTemplateDialog_Impl::Update_Impl() +{ + bool bDocChanged=false; + SfxStyleSheetBasePool* pNewPool = nullptr; + SfxViewFrame* pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame(); + SfxObjectShell* pDocShell = pViewFrame->GetObjectShell(); + if( pDocShell ) + pNewPool = pDocShell->GetStyleSheetPool(); + + if ( pNewPool != pStyleSheetPool && pDocShell ) + { + SfxModule* pNewModule = pDocShell->GetModule(); + if( pNewModule && pNewModule != pModule ) + { + ClearResource(); + ReadResource(); + } + if ( pStyleSheetPool ) + { + EndListening(*pStyleSheetPool); + pStyleSheetPool = nullptr; + } + + if ( pNewPool ) + { + StartListening(*pNewPool); + pStyleSheetPool = pNewPool; + bDocChanged=true; + } + } + + if (bUpdateFamily) + UpdateFamily_Impl(); + + sal_uInt16 i; + for(i = 0; i < MAX_FAMILIES; ++i) + if(pFamilyState[i]) + break; + if(i == MAX_FAMILIES || !pNewPool) + // nothing is allowed + return; + + SfxTemplateItem *pItem = nullptr; + // current region not within the allowed region or default + if(nActFamily == 0xffff || nullptr == (pItem = pFamilyState[nActFamily-1].get() ) ) + { + CheckItem(OString::number(nActFamily), false); + const size_t nFamilyCount = pStyleFamilies->size(); + size_t n; + for( n = 0; n < nFamilyCount; n++ ) + if( pFamilyState[ StyleNrToInfoOffset(n) ] ) break; + + std::unique_ptr<SfxTemplateItem> & pNewItem = pFamilyState[StyleNrToInfoOffset(n)]; + nAppFilter = pNewItem->GetValue(); + FamilySelect( StyleNrToInfoOffset(n) + 1 ); + pItem = pNewItem.get(); + } + else if( bDocChanged ) + { + // other DocShell -> all new + CheckItem(OString::number(nActFamily)); + nActFilter = static_cast< sal_uInt16 >( LoadFactoryStyleFilter( pDocShell ) ); + if ( 0xffff == nActFilter ) + nActFilter = pDocShell->GetAutoStyleFilterIndex(); + + nAppFilter = pItem->GetValue(); + if (!mxTreeBox->get_visible()) + { + UpdateStyles_Impl(StyleFlags::UpdateFamilyList); + } + else + FillTreeBox(); + } + else + { + // other filters for automatic + CheckItem(OString::number(nActFamily)); + const SfxStyleFamilyItem *pStyleItem = GetFamilyItem_Impl(); + if ( pStyleItem && SfxStyleSearchBits::Auto == pStyleItem->GetFilterList()[nActFilter].nFlags + && nAppFilter != pItem->GetValue()) + { + nAppFilter = pItem->GetValue(); + if (!mxTreeBox->get_visible()) + UpdateStyles_Impl(StyleFlags::UpdateFamilyList); + else + FillTreeBox(); + } + else + nAppFilter = pItem->GetValue(); + } + const OUString aStyle(pItem->GetStyleName()); + SelectStyle(aStyle, false); + EnableDelete(); + EnableNew( bCanNew ); +} + +IMPL_LINK_NOARG( SfxCommonTemplateDialog_Impl, TimeOut, Timer *, void ) +{ + if(!bDontUpdate) + { + bDontUpdate=true; + if (!mxTreeBox->get_visible()) + UpdateStyles_Impl(StyleFlags::UpdateFamilyList); + else + { + FillTreeBox(); + SfxTemplateItem *pState = pFamilyState[nActFamily-1].get(); + if(pState) + { + SelectStyle(pState->GetStyleName(), false); + EnableDelete(); + } + } + bDontUpdate=false; + pIdle.reset(); + } + else + pIdle->Start(); +} + +void SfxCommonTemplateDialog_Impl::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + const SfxHintId nId = rHint.GetId(); + + // tap update + switch(nId) + { + case SfxHintId::UpdateDone: + { + SfxViewFrame *pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame(); + SfxObjectShell *pDocShell = pViewFrame->GetObjectShell(); + if ( + bUpdate && + ( + !IsCheckedItem("watercan") || + (pDocShell && pDocShell->GetStyleSheetPool() != pStyleSheetPool) + ) + ) + { + bUpdate = false; + Update_Impl(); + } + else if ( bUpdateFamily ) + { + UpdateFamily_Impl(); + } + + if( pStyleSheetPool ) + { + OUString aStr = GetSelectedEntry(); + if (!aStr.isEmpty()) + { + const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl(); + if( !pItem ) break; + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSheetBase *pStyle = pStyleSheetPool->Find( aStr, eFam ); + if( pStyle ) + { + bool bReadWrite = !(pStyle->GetMask() & SfxStyleSearchBits::ReadOnly); + EnableEdit( bReadWrite ); + EnableHide( bReadWrite && !pStyle->IsUsed( ) && !pStyle->IsHidden( ) ); + EnableShow( bReadWrite && pStyle->IsHidden( ) ); + } + else + { + EnableEdit(false); + EnableHide(false); + EnableShow(false); + } + } + } + break; + } + + // Necessary if switching between documents and in both documents + // the same template is used. Do not immediately call Update_Impl, + // for the case that one of the documents is an internal InPlaceObject! + case SfxHintId::DocChanged: + bUpdate = true; + break; + case SfxHintId::Dying: + { + EndListening(*pStyleSheetPool); + pStyleSheetPool=nullptr; + break; + } + default: break; + } + + // Do not set timer when the stylesheet pool is in the box, because it is + // possible that a new one is registered after the timer is up - + // works bad in UpdateStyles_Impl ()! + + if(!bDontUpdate && nId != SfxHintId::Dying && + (dynamic_cast<const SfxStyleSheetPoolHint*>(&rHint) || + dynamic_cast<const SfxStyleSheetHint*>(&rHint) || + dynamic_cast<const SfxStyleSheetModifiedHint*>(&rHint) || + nId == SfxHintId::StyleSheetModified)) + { + if(!pIdle) + { + pIdle.reset(new Idle("SfxCommonTemplate")); + pIdle->SetPriority(TaskPriority::LOWEST); + pIdle->SetInvokeHandler(LINK(this,SfxCommonTemplateDialog_Impl,TimeOut)); + } + pIdle->Start(); + + } +} + +// Other filters; can be switched by the users or as a result of new or +// editing, if the current document has been assigned a different filter. +void SfxCommonTemplateDialog_Impl::FilterSelect( + sal_uInt16 nEntry, // Idx of the new Filters + bool bForce ) // Force update, even if the new filter is + // equal to the current +{ + if( nEntry == nActFilter && !bForce ) + return; + + nActFilter = nEntry; + SfxObjectShell *const pDocShell = SaveSelection(); + SfxStyleSheetBasePool *pOldStyleSheetPool = pStyleSheetPool; + pStyleSheetPool = pDocShell? pDocShell->GetStyleSheetPool(): nullptr; + if ( pOldStyleSheetPool != pStyleSheetPool ) + { + if ( pOldStyleSheetPool ) + EndListening(*pOldStyleSheetPool); + if ( pStyleSheetPool ) + StartListening(*pStyleSheetPool); + } + + UpdateStyles_Impl(StyleFlags::UpdateFamilyList); +} + +// Internal: Perform functions through the Dispatcher +bool SfxCommonTemplateDialog_Impl::Execute_Impl( + sal_uInt16 nId, const OUString &rStr, const OUString& rRefStr, sal_uInt16 nFamily, + SfxStyleSearchBits nMask, sal_uInt16 *pIdx, const sal_uInt16* pModifier) +{ + SfxDispatcher &rDispatcher = *SfxGetpApp()->GetDispatcher_Impl(); + SfxStringItem aItem(nId, rStr); + SfxUInt16Item aFamily(SID_STYLE_FAMILY, nFamily); + SfxUInt16Item aMask( SID_STYLE_MASK, static_cast<sal_uInt16>(nMask) ); + SfxStringItem aUpdName(SID_STYLE_UPD_BY_EX_NAME, rStr); + SfxStringItem aRefName( SID_STYLE_REFERENCE, rRefStr ); + const SfxPoolItem* pItems[ 6 ]; + sal_uInt16 nCount = 0; + if( !rStr.isEmpty() ) + pItems[ nCount++ ] = &aItem; + pItems[ nCount++ ] = &aFamily; + if( nMask != SfxStyleSearchBits::Auto ) + pItems[ nCount++ ] = &aMask; + if(SID_STYLE_UPDATE_BY_EXAMPLE == nId) + { + // Special solution for Numbering update in Writer + const OUString aTemplName(GetSelectedEntry()); + aUpdName.SetValue(aTemplName); + pItems[ nCount++ ] = &aUpdName; + } + + if ( !rRefStr.isEmpty() ) + pItems[ nCount++ ] = &aRefName; + + pItems[ nCount++ ] = nullptr; + + DeletionWatcher aDeleted(*this); + sal_uInt16 nModi = pModifier ? *pModifier : 0; + const SfxPoolItem* pItem = rDispatcher.Execute( + nId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + pItems, nModi ); + + // Dialog can be destroyed while in Execute() because started + // subdialogs are not modal to it (#i97888#). + if ( !pItem || aDeleted ) + return false; + + if ( (nId == SID_STYLE_NEW || SID_STYLE_EDIT == nId) && (mxTreeBox->get_visible() || mxFmtLb->count_selected_rows() <= 1) ) + { + const SfxUInt16Item *pFilterItem = dynamic_cast< const SfxUInt16Item* >(pItem); + assert(pFilterItem); + SfxStyleSearchBits nFilterFlags = static_cast<SfxStyleSearchBits>(pFilterItem->GetValue()) & ~SfxStyleSearchBits::UserDefined; + if(nFilterFlags == SfxStyleSearchBits::Auto) // User Template? + nFilterFlags = static_cast<SfxStyleSearchBits>(pFilterItem->GetValue()); + const SfxStyleFamilyItem *pFamilyItem = GetFamilyItem_Impl(); + const size_t nFilterCount = pFamilyItem->GetFilterList().size(); + + for ( size_t i = 0; i < nFilterCount; ++i ) + { + const SfxFilterTuple &rTupel = pFamilyItem->GetFilterList()[ i ]; + + if ( ( rTupel.nFlags & nFilterFlags ) == nFilterFlags && pIdx ) + *pIdx = i; + } + } + + return true; +} + +// Handler Listbox of Filter +void SfxCommonTemplateDialog_Impl::EnableHierarchical(bool const bEnable) +{ + if (bEnable) + { + if (!bHierarchical) + { + // Turn on treeView + bHierarchical=true; + m_bWantHierarchical = true; + SaveSelection(); // fdo#61429 store "hierarchical" + const OUString aSelectEntry( GetSelectedEntry()); + mxFmtLb->hide(); + FillTreeBox(); + SelectStyle(aSelectEntry, false); + mxTreeBox->show(); + } + } + else + { + mxTreeBox->hide(); + mxFmtLb->show(); + // If bHierarchical, then the family can have changed + // minus one since hierarchical is inserted at the start + m_bWantHierarchical = false; // before FilterSelect + FilterSelect(mxFilterLb->get_active() - 1, bHierarchical ); + bHierarchical=false; + } +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, FilterSelectHdl, weld::ComboBox&, rBox, void) +{ + if (SfxResId(STR_STYLE_FILTER_HIERARCHICAL) == rBox.get_active_text()) + { + EnableHierarchical(true); + } + else + { + EnableHierarchical(false); + } +} + +// Select-Handler for the Toolbox +void SfxCommonTemplateDialog_Impl::FamilySelect(sal_uInt16 nEntry, bool bPreviewRefresh) +{ + assert((0 < nEntry && nEntry <= MAX_FAMILIES) || 0xffff == nEntry); + if( nEntry != nActFamily || bPreviewRefresh ) + { + CheckItem(OString::number(nActFamily), false); + nActFamily = nEntry; + SfxDispatcher* pDispat = pBindings->GetDispatcher_Impl(); + SfxUInt16Item const aItem(SID_STYLE_FAMILY, + static_cast<sal_uInt16>(SfxTemplate::NIdToSfxFamilyId(nEntry))); + pDispat->ExecuteList(SID_STYLE_FAMILY, SfxCallMode::SYNCHRON, { &aItem }); + pBindings->Invalidate( SID_STYLE_FAMILY ); + pBindings->Update( SID_STYLE_FAMILY ); + UpdateFamily_Impl(); + } +} + +void SfxCommonTemplateDialog_Impl::ActionSelect(const OString& rEntry) +{ + if (rEntry == "watercan") + { + const bool bOldState = !IsCheckedItem(rEntry); + bool bCheck; + SfxBoolItem aBool; + // when a template is chosen. + if (!bOldState && HasSelectedStyle()) + { + const OUString aTemplName( + GetSelectedEntry()); + Execute_Impl( + SID_STYLE_WATERCAN, aTemplName, "", + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) ); + bCheck = true; + } + else + { + Execute_Impl(SID_STYLE_WATERCAN, "", "", 0); + bCheck = false; + } + CheckItem(rEntry, bCheck); + aBool.SetValue(bCheck); + SetWaterCanState(&aBool); + } + else if (rEntry == "new" || rEntry == "newmenu") + { + if(pStyleSheetPool && nActFamily != 0xffff) + { + const SfxStyleFamily eFam=GetFamilyItem_Impl()->GetFamily(); + const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl(); + SfxStyleSearchBits nFilter(SfxStyleSearchBits::Auto); + if (pItem && nActFilter != 0xffff) + nFilter = pItem->GetFilterList()[nActFilter].nFlags; + if (nFilter == SfxStyleSearchBits::Auto) // automatic + nFilter = nAppFilter; + + // why? : FloatingWindow must not be parent of a modal dialog + SfxNewStyleDlg aDlg(pWindow ? pWindow->GetFrameWeld() : nullptr, *pStyleSheetPool, eFam); + auto nResult = aDlg.run(); + if (nResult == RET_OK) + { + const OUString aTemplName(aDlg.GetName()); + Execute_Impl(SID_STYLE_NEW_BY_EXAMPLE, + aTemplName, "", + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()), + nFilter); + UpdateFamily_Impl(); + } + } + } + else if (rEntry == "update") + { + Execute_Impl(SID_STYLE_UPDATE_BY_EXAMPLE, + "", "", + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily())); + } + else if (rEntry == "load") + SfxGetpApp()->GetDispatcher_Impl()->Execute(SID_TEMPLATE_LOAD); + else + SAL_WARN("sfx", "not implemented: " << rEntry); +} + +static OUString getModuleIdentifier( const Reference< XModuleManager2 >& i_xModMgr, SfxObjectShell const * i_pObjSh ) +{ + OSL_ENSURE( i_xModMgr.is(), "getModuleIdentifier(): no XModuleManager" ); + OSL_ENSURE( i_pObjSh, "getModuleIdentifier(): no ObjectShell" ); + + OUString sIdentifier; + + try + { + sIdentifier = i_xModMgr->identify( i_pObjSh->GetModel() ); + } + catch ( css::frame::UnknownModuleException& ) + { + SAL_WARN("sfx", "getModuleIdentifier(): unknown module" ); + } + catch ( Exception& ) + { + OSL_FAIL( "getModuleIdentifier(): exception of XModuleManager::identify()" ); + } + + return sIdentifier; +} + +sal_Int32 SfxCommonTemplateDialog_Impl::LoadFactoryStyleFilter( SfxObjectShell const * i_pObjSh ) +{ + OSL_ENSURE( i_pObjSh, "SfxCommonTemplateDialog_Impl::LoadFactoryStyleFilter(): no ObjectShell" ); + + ::comphelper::SequenceAsHashMap aFactoryProps( + xModuleManager->getByName( getModuleIdentifier( xModuleManager, i_pObjSh ) ) ); + sal_Int32 nFilter = aFactoryProps.getUnpackedValueOrDefault( "ooSetupFactoryStyleFilter", sal_Int32(-1) ); + + m_bWantHierarchical = (nFilter & 0x1000) != 0; + nFilter &= ~0x1000; // clear it + + return nFilter; +} + +void SfxCommonTemplateDialog_Impl::SaveFactoryStyleFilter( SfxObjectShell const * i_pObjSh, sal_Int32 i_nFilter ) +{ + OSL_ENSURE( i_pObjSh, "SfxCommonTemplateDialog_Impl::LoadFactoryStyleFilter(): no ObjectShell" ); + Sequence< PropertyValue > lProps(1); + lProps[0].Name = "ooSetupFactoryStyleFilter"; + lProps[0].Value <<= i_nFilter | (m_bWantHierarchical ? 0x1000 : 0); + xModuleManager->replaceByName( getModuleIdentifier( xModuleManager, i_pObjSh ), makeAny( lProps ) ); +} + +SfxObjectShell* SfxCommonTemplateDialog_Impl::SaveSelection() +{ + SfxViewFrame *const pViewFrame(pBindings->GetDispatcher_Impl()->GetFrame()); + SfxObjectShell *const pDocShell(pViewFrame->GetObjectShell()); + if (pDocShell) + { + pDocShell->SetAutoStyleFilterIndex(nActFilter); + SaveFactoryStyleFilter( pDocShell, nActFilter ); + } + return pDocShell; +} + +void SfxCommonTemplateDialog_Impl::DropHdl(const OUString& rStyle, const OUString& rParent) +{ + bDontUpdate = true; + const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl(); + const SfxStyleFamily eFam = pItem->GetFamily(); + pStyleSheetPool->SetParent(eFam, rStyle, rParent); + bDontUpdate = false; +} + +// Handler for the New-Buttons +void SfxCommonTemplateDialog_Impl::NewHdl() +{ + if ( nActFamily == 0xffff || !(mxTreeBox->get_visible() || mxFmtLb->count_selected_rows() <= 1)) + return; + + const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl(); + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSearchBits nMask(SfxStyleSearchBits::Auto); + if (nActFilter != 0xffff) + nMask = pItem->GetFilterList()[nActFilter].nFlags; + if (nMask == SfxStyleSearchBits::Auto) // automatic + nMask = nAppFilter; + + Execute_Impl(SID_STYLE_NEW, + "", GetSelectedEntry(), + static_cast<sal_uInt16>(eFam), + nMask); +} + +// Handler for the edit-Buttons +void SfxCommonTemplateDialog_Impl::EditHdl() +{ + if(IsInitialized() && HasSelectedStyle()) + { + sal_uInt16 nFilter = nActFilter; + OUString aTemplName(GetSelectedEntry()); + GetSelectedStyle(); // -Wall required?? + Execute_Impl( SID_STYLE_EDIT, aTemplName, OUString(), + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()), SfxStyleSearchBits::Auto, &nFilter ); + } +} + +// Handler for the Delete-Buttons +void SfxCommonTemplateDialog_Impl::DeleteHdl() +{ + if ( !IsInitialized() || !HasSelectedStyle() ) + return; + + bool bUsedStyle = false; // one of the selected styles are used in the document? + + std::vector<std::unique_ptr<weld::TreeIter>> aList; + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + + OUStringBuffer aMsg; + aMsg.append(SfxResId(STR_DELETE_STYLE_USED)).append(SfxResId(STR_DELETE_STYLE)); + + pTreeView->selected_foreach([this, pTreeView, pItem, &aList, &bUsedStyle, &aMsg](weld::TreeIter& rEntry){ + aList.emplace_back(pTreeView->make_iterator(&rEntry)); + // check the style is used or not + const OUString aTemplName(pTreeView->get_text(rEntry)); + + SfxStyleSheetBase* pStyle = pStyleSheetPool->Find( aTemplName, pItem->GetFamily() ); + + if ( pStyle->IsUsed() ) // pStyle is in use in the document? + { + if (bUsedStyle) // add a separator for the second and later styles + aMsg.append(", "); + aMsg.append(aTemplName); + bUsedStyle = true; + } + + return false; + }); + + bool aApproved = false; + + // we only want to show the dialog once and if we want to delete a style in use (UX-advice) + if (bUsedStyle) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pTreeView, + VclMessageType::Question, VclButtonsType::YesNo, + aMsg.makeStringAndClear())); + aApproved = xBox->run() == RET_YES; + } + + // if there are no used styles selected or the user approved the changes + if ( !(!bUsedStyle || aApproved) ) + return; + + for (auto const& elem : aList) + { + const OUString aTemplName(pTreeView->get_text(*elem)); + bDontUpdate = true; // To prevent the Treelistbox to shut down while deleting + Execute_Impl( SID_STYLE_DELETE, aTemplName, + OUString(), static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) ); + + if (mxTreeBox->get_visible()) + { + weld::RemoveParentKeepChildren(*mxTreeBox, *elem); + bDontUpdate = false; + } + } + bDontUpdate = false; //if everything is deleted set bDontUpdate back to false + UpdateStyles_Impl(StyleFlags::UpdateFamilyList); //and force-update the list +} + +void SfxCommonTemplateDialog_Impl::HideHdl() +{ + if ( !IsInitialized() || !HasSelectedStyle() ) + return; + + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + pTreeView->selected_foreach([this, pTreeView](weld::TreeIter& rEntry){ + OUString aTemplName = pTreeView->get_text(rEntry); + + Execute_Impl( SID_STYLE_HIDE, aTemplName, + OUString(), static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) ); + + return false; + }); +} + +void SfxCommonTemplateDialog_Impl::ShowHdl() +{ + if ( !IsInitialized() || !HasSelectedStyle() ) + return; + + weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get(); + pTreeView->selected_foreach([this, pTreeView](weld::TreeIter& rEntry){ + OUString aTemplName = pTreeView->get_text(rEntry); + + Execute_Impl( SID_STYLE_SHOW, aTemplName, + OUString(), static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) ); + + return false; + }); +} + +void SfxCommonTemplateDialog_Impl::EnableDelete() +{ + bool bEnableDelete(false); + if(IsInitialized() && HasSelectedStyle()) + { + OSL_ENSURE(pStyleSheetPool, "No StyleSheetPool"); + const OUString aTemplName(GetSelectedEntry()); + const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl(); + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSearchBits nFilter = SfxStyleSearchBits::Auto; + if (pItem->GetFilterList().size() > nActFilter) + nFilter = pItem->GetFilterList()[nActFilter].nFlags; + if(nFilter == SfxStyleSearchBits::Auto) // automatic + nFilter = nAppFilter; + const SfxStyleSheetBase *pStyle = + pStyleSheetPool->Find(aTemplName,eFam, mxTreeBox->get_visible() ? SfxStyleSearchBits::All : nFilter); + + OSL_ENSURE(pStyle, "Style not found"); + if (pStyle && pStyle->IsUserDefined()) + { + if (pStyle->HasClearParentSupport() || !pStyle->IsUsed()) + { + bEnableDelete = true; + } + else if (pStyle->GetFamily() == SfxStyleFamily::Page) + { + // Hack to allow Calc page styles to be deleted, + // remove when IsUsed is fixed for Calc page styles. + SfxViewFrame* pFrame = GetObjectShell()->GetFrame(); + if (pFrame) + { + uno::Reference<frame::XFrame > xFrame = pFrame->GetFrame().GetFrameInterface(); + if (vcl::CommandInfoProvider::GetModuleIdentifier(xFrame) == "com.sun.star.sheet.SpreadsheetDocument") + { + bEnableDelete = true; + } + } + } + } + } + EnableDel(bEnableDelete); +} + +IMPL_LINK(SfxCommonTemplateDialog_Impl, MousePressHdl, const MouseEvent&, rMEvt, bool) +{ + m_nModifier = rMEvt.GetModifier(); + return false; +} + +// Double-click on a style sheet in the ListBox is applied. +IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, TreeListApplyHdl, weld::TreeView&, bool) +{ + // only if that region is allowed + if ( IsInitialized() && nullptr != pFamilyState[nActFamily-1] && + !GetSelectedEntry().isEmpty() ) + { + Execute_Impl(SID_STYLE_APPLY, + GetSelectedEntry(), OUString(), + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()), + SfxStyleSearchBits::Auto, nullptr, &m_nModifier); + } + // After selecting a focused item if possible again on the app window + if ( dynamic_cast< const SfxTemplateDialog_Impl* >(this) != nullptr ) + { + SfxViewFrame *pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame(); + SfxViewShell *pVu = pViewFrame->GetViewShell(); + vcl::Window *pAppWin = pVu ? pVu->GetWindow(): nullptr; + if(pAppWin) + pAppWin->GrabFocus(); + } + + return true; +} + +IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, PreviewHdl, weld::Button&, void) +{ + std::shared_ptr<comphelper::ConfigurationChanges> batch( comphelper::ConfigurationChanges::create() ); + bool bCustomPreview = mxPreviewCheckbox->get_active(); + officecfg::Office::Common::StylesAndFormatting::Preview::set(bCustomPreview, batch ); + batch->commit(); + + mxFmtLb->clear(); + mxFmtLb->set_column_custom_renderer(0, bCustomPreview); + mxTreeBox->clear(); + mxTreeBox->set_column_custom_renderer(0, bCustomPreview); + + FamilySelect(nActFamily, true); +} + +// Selection of a template during the Watercan-Status +IMPL_LINK(SfxCommonTemplateDialog_Impl, FmtSelectHdl, weld::TreeView&, rListBox, void) +{ + FmtSelect(&rListBox, true); +} + +void SfxCommonTemplateDialog_Impl::FmtSelect(weld::TreeView* pListBox, bool bIsCallback) +{ + std::unique_ptr<weld::TreeIter> xHdlEntry; + if (pListBox) + { + xHdlEntry = pListBox->make_iterator(); + if (!pListBox->get_cursor(xHdlEntry.get())) + return; + } + + // Trigger Help PI, if this is permitted of call handlers and field + if (!pListBox || pListBox->is_selected(*xHdlEntry)) + { + // Only when the watercan is on + if ( IsInitialized() && + IsCheckedItem("watercan") && + // only if that region is allowed + nullptr != pFamilyState[nActFamily-1] && (mxTreeBox || mxFmtLb->count_selected_rows() <= 1) ) + { + Execute_Impl(SID_STYLE_WATERCAN, + "", "", 0); + Execute_Impl(SID_STYLE_WATERCAN, + GetSelectedEntry(), "", + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily())); + } + EnableItem("watercan", !bWaterDisabled); + EnableDelete(); + } + if( !pListBox ) + return; + + SelectStyle(pListBox->get_text(*xHdlEntry), bIsCallback); +} + +void SfxCommonTemplateDialog_Impl::MenuSelect(const OString& rIdent) +{ + sLastItemIdent = rIdent; + if (sLastItemIdent.isEmpty()) + return; + Application::PostUserEvent( + LINK(this, SfxCommonTemplateDialog_Impl, MenuSelectAsyncHdl)); +} + +IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, MenuSelectAsyncHdl, void*, void) +{ + if (sLastItemIdent == "new") + NewHdl(); + else if (sLastItemIdent == "edit") + EditHdl(); + else if (sLastItemIdent == "delete") + DeleteHdl(); + else if (sLastItemIdent == "hide") + HideHdl(); + else if (sLastItemIdent == "show") + ShowHdl(); +} + +SfxStyleFamily SfxCommonTemplateDialog_Impl::GetActualFamily() const +{ + const SfxStyleFamilyItem *pFamilyItem = GetFamilyItem_Impl(); + if( !pFamilyItem || nActFamily == 0xffff ) + return SfxStyleFamily::Para; + else + return pFamilyItem->GetFamily(); +} + +void SfxCommonTemplateDialog_Impl::EnableExample_Impl(sal_uInt16 nId, bool bEnable) +{ + bool bDisable = !bEnable || !IsSafeForWaterCan(); + if (nId == SID_STYLE_NEW_BY_EXAMPLE) + { + bNewByExampleDisabled = bDisable; + EnableItem("new", bEnable); + EnableItem("newmenu", bEnable); + } + else if( nId == SID_STYLE_UPDATE_BY_EXAMPLE ) + { + bUpdateByExampleDisabled = bDisable; + EnableItem("update", bEnable); + } +} + +void SfxCommonTemplateDialog_Impl::CreateContextMenu() +{ + if ( bBindingUpdate ) + { + pBindings->Invalidate( SID_STYLE_NEW, true ); + pBindings->Update( SID_STYLE_NEW ); + bBindingUpdate = false; + } + mxMenu.reset(); + mxMenuBuilder.reset(Application::CreateBuilder(nullptr, "sfx/ui/stylecontextmenu.ui")); + mxMenu = mxMenuBuilder->weld_menu("menu"); + mxMenu->set_sensitive("edit", bCanEdit); + mxMenu->set_sensitive("delete", bCanDel); + mxMenu->set_sensitive("new", bCanNew); + mxMenu->set_sensitive("hide", bCanHide); + mxMenu->set_sensitive("show", bCanShow); + + const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl(); + if (pItem && pItem->GetFamily() == SfxStyleFamily::Table) //tdf#101648, no ui for this yet + { + mxMenu->set_sensitive("edit", false); + mxMenu->set_sensitive("new", false); + } +} + +SfxTemplateDialog_Impl::SfxTemplateDialog_Impl(SfxBindings* pB, SfxTemplatePanelControl* pDlgWindow) + : SfxCommonTemplateDialog_Impl(pB, pDlgWindow, pDlgWindow->get_builder()) + , m_xActionTbL(pDlgWindow->get_builder()->weld_toolbar("left")) + , m_xActionTbR(pDlgWindow->get_builder()->weld_toolbar("right")) + , m_xToolMenu(pDlgWindow->get_builder()->weld_menu("toolmenu")) + , m_nActionTbLVisible(0) +{ + m_xActionTbR->set_item_help_id("watercan", HID_TEMPLDLG_WATERCAN); + // shown/hidden in SfxTemplateDialog_Impl::ReplaceUpdateButtonByMenu() + m_xActionTbR->set_item_help_id("new", HID_TEMPLDLG_NEWBYEXAMPLE); + m_xActionTbR->set_item_help_id("newmenu", HID_TEMPLDLG_NEWBYEXAMPLE); + m_xActionTbR->set_item_menu("newmenu", m_xToolMenu.get()); + m_xToolMenu->connect_activate(LINK(this, SfxTemplateDialog_Impl, ToolMenuSelectHdl)); + m_xActionTbR->set_item_help_id("update", HID_TEMPLDLG_UPDATEBYEXAMPLE); + + Initialize(); +} + +class ToolbarDropTarget final : public DropTargetHelper +{ +private: + SfxTemplateDialog_Impl& m_rParent; + +public: + ToolbarDropTarget(SfxTemplateDialog_Impl& rDialog, weld::Toolbar& rToolbar) + : DropTargetHelper(rToolbar.get_drop_target()) + , m_rParent(rDialog) + { + } + + virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override + { + return m_rParent.AcceptToolbarDrop(rEvt, *this); + } + + virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override + { + return m_rParent.ExecuteDrop(rEvt); + } +}; + +void SfxTemplateDialog_Impl::Initialize() +{ + SfxCommonTemplateDialog_Impl::Initialize(); + + m_xActionTbL->connect_clicked(LINK(this, SfxTemplateDialog_Impl, ToolBoxLSelect)); + m_xActionTbR->connect_clicked(LINK(this, SfxTemplateDialog_Impl, ToolBoxRSelect)); + m_xActionTbL->set_help_id(HID_TEMPLDLG_TOOLBOX_LEFT); + + m_xToolbarDropTargetHelper.reset(new ToolbarDropTarget(*this, *m_xActionTbL)); +} + +void SfxTemplateDialog_Impl::EnableFamilyItem(sal_uInt16 nId, bool bEnable) +{ + m_xActionTbL->set_item_sensitive(OString::number(nId), bEnable); +} + +// Insert element into dropdown filter "Frame Styles", "List Styles", etc. +void SfxTemplateDialog_Impl::InsertFamilyItem(sal_uInt16 nId, const SfxStyleFamilyItem &rItem) +{ + OString sHelpId; + switch( rItem.GetFamily() ) + { + case SfxStyleFamily::Char: sHelpId = ".uno:CharStyle"; break; + case SfxStyleFamily::Para: sHelpId = ".uno:ParaStyle"; break; + case SfxStyleFamily::Frame: sHelpId = ".uno:FrameStyle"; break; + case SfxStyleFamily::Page: sHelpId = ".uno:PageStyle"; break; + case SfxStyleFamily::Pseudo: sHelpId = ".uno:ListStyle"; break; + case SfxStyleFamily::Table: sHelpId = ".uno:TableStyle"; break; + default: OSL_FAIL("unknown StyleFamily"); break; + } + + OString sId(OString::number(nId)); + m_xActionTbL->set_item_visible(sId, true); + m_xActionTbL->set_item_icon_name(sId, rItem.GetImage()); + m_xActionTbL->set_item_tooltip_text(sId, rItem.GetText()); + m_xActionTbL->set_item_help_id(sId, sHelpId); + ++m_nActionTbLVisible; +} + +void SfxTemplateDialog_Impl::ReplaceUpdateButtonByMenu() +{ + m_xActionTbR->set_item_visible("update", false); + m_xActionTbR->set_item_visible("new", false); + m_xActionTbR->set_item_visible("newmenu", true); + FillToolMenu(); +} + +void SfxTemplateDialog_Impl::ClearFamilyList() +{ + for (int i = 0, nCount = m_xActionTbL->get_n_items(); i < nCount; ++i) + m_xActionTbL->set_item_visible(m_xActionTbL->get_item_ident(i), false); + +} + +void SfxCommonTemplateDialog_Impl::InvalidateBindings() +{ + pBindings->Invalidate(SID_STYLE_NEW_BY_EXAMPLE, true); + pBindings->Update( SID_STYLE_NEW_BY_EXAMPLE ); + pBindings->Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE, true); + pBindings->Update( SID_STYLE_UPDATE_BY_EXAMPLE ); + pBindings->Invalidate( SID_STYLE_WATERCAN, true); + pBindings->Update( SID_STYLE_WATERCAN ); + pBindings->Invalidate( SID_STYLE_NEW, true); + pBindings->Update( SID_STYLE_NEW ); + pBindings->Invalidate( SID_STYLE_DRAGHIERARCHIE, true); + pBindings->Update( SID_STYLE_DRAGHIERARCHIE ); +} + +SfxTemplateDialog_Impl::~SfxTemplateDialog_Impl() +{ + m_xToolbarDropTargetHelper.reset(); + m_xActionTbL.reset(); + m_xActionTbR.reset(); +} + +void SfxTemplateDialog_Impl::EnableItem(const OString& rMesId, bool bCheck) +{ + if (rMesId == "watercan" && !bCheck && IsCheckedItem("watercan")) + Execute_Impl(SID_STYLE_WATERCAN, "", "", 0); + m_xActionTbR->set_item_sensitive(rMesId, bCheck); +} + +void SfxTemplateDialog_Impl::CheckItem(const OString &rMesId, bool bCheck) +{ + if (rMesId == "watercan") + { + bIsWater=bCheck; + m_xActionTbR->set_item_active("watercan", bCheck); + } + else + m_xActionTbL->set_item_active(rMesId, bCheck); +} + +bool SfxTemplateDialog_Impl::IsCheckedItem(const OString& rMesId) +{ + if (rMesId == "watercan") + return m_xActionTbR->get_item_active("watercan"); + return m_xActionTbL->get_item_active(rMesId); +} + +IMPL_LINK( SfxTemplateDialog_Impl, ToolBoxLSelect, const OString&, rEntry, void) +{ + FamilySelect(rEntry.toUInt32()); +} + +IMPL_LINK(SfxTemplateDialog_Impl, ToolBoxRSelect, const OString&, rEntry, void) +{ + if (rEntry == "newmenu") + m_xActionTbR->set_menu_item_active(rEntry, !m_xActionTbR->get_menu_item_active(rEntry)); + else + ActionSelect(rEntry); +} + +void SfxTemplateDialog_Impl::FillToolMenu() +{ + //create a popup menu in Writer + OUString sTextDoc("com.sun.star.text.TextDocument"); + + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:StyleNewByExample", sTextDoc); + OUString sLabel = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties); + m_xToolMenu->append("new", sLabel); + aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:StyleUpdateByExample", sTextDoc); + sLabel = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties); + m_xToolMenu->append("update", sLabel); + m_xToolMenu->append_separator("separator"); + + aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:LoadStyles", sTextDoc); + sLabel = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties); + m_xToolMenu->append("load", sLabel); +} + +IMPL_LINK(SfxTemplateDialog_Impl, ToolMenuSelectHdl, const OString&, rMenuId, void) +{ + if (rMenuId.isEmpty()) + return; + ActionSelect(rMenuId); +} + +void SfxCommonTemplateDialog_Impl::SetFamily(SfxStyleFamily const nFamily) +{ + sal_uInt16 const nId(SfxTemplate::SfxFamilyIdToNId(nFamily)); + assert((0 < nId && nId <= MAX_FAMILIES) || 0xffff == nId); + if ( nId != nActFamily ) + { + if ( nActFamily != 0xFFFF ) + CheckItem(OString::number(nActFamily), false); + nActFamily = nId; + if ( nId != 0xFFFF ) + bUpdateFamily = true; + } +} + +void SfxCommonTemplateDialog_Impl::UpdateFamily_Impl() +{ + bUpdateFamily = false; + + SfxDispatcher* pDispat = pBindings->GetDispatcher_Impl(); + SfxViewFrame *pViewFrame = pDispat->GetFrame(); + SfxObjectShell *pDocShell = pViewFrame->GetObjectShell(); + + SfxStyleSheetBasePool *pOldStyleSheetPool = pStyleSheetPool; + pStyleSheetPool = pDocShell? pDocShell->GetStyleSheetPool(): nullptr; + if ( pOldStyleSheetPool != pStyleSheetPool ) + { + if ( pOldStyleSheetPool ) + EndListening(*pOldStyleSheetPool); + if ( pStyleSheetPool ) + StartListening(*pStyleSheetPool); + } + + bWaterDisabled = false; + bCanNew = mxTreeBox->get_visible() || mxFmtLb->count_selected_rows() <= 1; + bTreeDrag = true; + bUpdateByExampleDisabled = false; + + if (pStyleSheetPool) + { + if (!mxTreeBox->get_visible()) + UpdateStyles_Impl(StyleFlags::UpdateFamily | StyleFlags::UpdateFamilyList); + else + { + UpdateStyles_Impl(StyleFlags::UpdateFamily); + FillTreeBox(); + } + } + + InvalidateBindings(); + + if (IsCheckedItem("watercan") && + // only if that area is allowed + nullptr != pFamilyState[nActFamily - 1]) + { + Execute_Impl(SID_STYLE_APPLY, + GetSelectedEntry(), + OUString(), + static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily())); + } +} + +void SfxCommonTemplateDialog_Impl::ReplaceUpdateButtonByMenu() +{ + //does nothing +} + +sal_Int8 SfxTemplateDialog_Impl::AcceptToolbarDrop(const AcceptDropEvent& rEvt, const DropTargetHelper& rHelper) +{ + sal_Int8 nReturn = DND_ACTION_NONE; + + // auto flip to the category under the mouse + int nIndex = m_xActionTbL->get_drop_index(rEvt.maPosPixel); + if (nIndex >= m_nActionTbLVisible) + nIndex = m_nActionTbLVisible - 1; + + OString sIdent = m_xActionTbL->get_item_ident(nIndex); + if (!sIdent.isEmpty() && !m_xActionTbL->get_item_active(sIdent)) + ToolBoxLSelect(sIdent); + + // special case: page styles are allowed to create new styles by example + // but not allowed to be created by drag and drop + if (sIdent.toUInt32() != SfxTemplate::SfxFamilyIdToNId(SfxStyleFamily::Page) && + rHelper.IsDropFormatSupported(SotClipboardFormatId::OBJECTDESCRIPTOR) && + !bNewByExampleDisabled) + { + nReturn = DND_ACTION_COPY; + } + return nReturn; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |