diff options
Diffstat (limited to 'sfx2/source/dialog/StyleList.cxx')
-rw-r--r-- | sfx2/source/dialog/StyleList.cxx | 1778 |
1 files changed, 1778 insertions, 0 deletions
diff --git a/sfx2/source/dialog/StyleList.cxx b/sfx2/source/dialog/StyleList.cxx new file mode 100644 index 000000000..ded3822b1 --- /dev/null +++ b/sfx2/source/dialog/StyleList.cxx @@ -0,0 +1,1778 @@ +/* -*- 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 <unordered_map> + +#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/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> +#include <vcl/window.hxx> +#include <svl/intitem.hxx> +#include <svl/style.hxx> +#include <comphelper/processfactory.hxx> +#include <officecfg/Office/Common.hxx> + +#include <osl/diagnose.h> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.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 <sfx2/viewfrm.hxx> + +#include <comphelper/string.hxx> + +#include <sfx2/StyleManager.hxx> +#include <sfx2/StylePreviewRenderer.hxx> + +#include <StyleList.hxx> + +using namespace css; +using namespace css::beans; +using namespace css::frame; +using namespace css::uno; + +// Constructor + +StyleList::StyleList(weld::Builder* pBuilder, SfxBindings* pBindings, + SfxCommonTemplateDialog_Impl* Parent, weld::Container* pC, + OString treeviewname, OString flatviewname) + : m_bHierarchical(false) + , m_bAllowReParentDrop(false) + , m_bNewByExampleDisabled(false) + , m_bDontUpdate(false) + , m_bTreeDrag(true) + , m_bCanEdit(false) + , m_bCanHide(true) + , m_bCanShow(false) + , m_bCanNew(true) + , m_bUpdateFamily(false) + , m_bCanDel(false) + , m_bBindingUpdate(true) + , m_pStyleSheetPool(nullptr) + , m_nActFilter(0) + , m_xFmtLb(pBuilder->weld_tree_view(flatviewname)) + , m_xTreeBox(pBuilder->weld_tree_view(treeviewname)) + , m_pCurObjShell(nullptr) + , m_nActFamily(0xffff) + , m_nAppFilter(SfxStyleSearchBits::Auto) + , m_pParentDialog(Parent) + , m_pBindings(pBindings) + , m_Module(nullptr) + , m_nModifier(0) + , m_pContainer(pC) +{ + m_xFmtLb->set_help_id(HID_TEMPLATE_FMT); +} + +// Destructor + +StyleList::~StyleList() {} + +// Called in the destructor of Dialog +// Cleans up the StyleList individual components while closing the application +IMPL_LINK_NOARG(StyleList, Cleanup, void*, void) +{ + if (m_pStyleSheetPool) + EndListening(*m_pStyleSheetPool); + m_pStyleSheetPool = nullptr; + m_xTreeView1DropTargetHelper.reset(); + m_xTreeView2DropTargetHelper.reset(); + m_xTreeBox.reset(); + m_xFmtLb.reset(); + pIdle.reset(); +} + +void StyleList::CreateContextMenu() +{ + if (m_bBindingUpdate) + { + m_pBindings->Invalidate(SID_STYLE_NEW, true); + m_pBindings->Update(SID_STYLE_NEW); + m_bBindingUpdate = false; + } + mxMenu.reset(); + mxMenuBuilder = Application::CreateBuilder(nullptr, "sfx/ui/stylecontextmenu.ui"); + mxMenu = mxMenuBuilder->weld_menu("menu"); + mxMenu->set_sensitive("edit", m_bCanEdit); + mxMenu->set_sensitive("delete", m_bCanDel); + mxMenu->set_sensitive("new", m_bCanNew); + mxMenu->set_sensitive("hide", m_bCanHide); + mxMenu->set_sensitive("show", m_bCanShow); + + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + if (pItem && pItem->GetFamily() == SfxStyleFamily::Table) //tdf#101648, no ui for this yet + { + mxMenu->set_sensitive("edit", false); + mxMenu->set_sensitive("new", false); + } + if (pItem && pItem->GetFamily() == SfxStyleFamily::Pseudo) + { + const OUString aTemplName(GetSelectedEntry()); + if (aTemplName == "No List") + { + mxMenu->set_sensitive("edit", false); + mxMenu->set_sensitive("new", false); + mxMenu->set_sensitive("hide", false); + } + } +} + +IMPL_LINK_NOARG(StyleList, ReadResource, void*, size_t) +{ + // Read global user resource + for (auto& i : m_pFamilyState) + i.reset(); + + SfxViewFrame* pViewFrame = m_pBindings->GetDispatcher_Impl()->GetFrame(); + m_pCurObjShell = pViewFrame->GetObjectShell(); + m_Module = m_pCurObjShell ? m_pCurObjShell->GetModule() : nullptr; + if (m_Module) + m_xStyleFamilies = m_Module->CreateStyleFamilies(); + if (!m_xStyleFamilies) + m_xStyleFamilies.emplace(); + + m_nActFilter = 0xffff; + + if (m_pCurObjShell) + { + m_nActFilter = static_cast<sal_uInt16>(m_aLoadFactoryStyleFilter.Call(m_pCurObjShell)); + if (0xffff == m_nActFilter) + { + m_nActFilter = m_pCurObjShell->GetAutoStyleFilterIndex(); + } + } + size_t nCount = m_xStyleFamilies->size(); + m_pBindings->ENTERREGISTRATIONS(); + + size_t i; + for (i = 0; i < nCount; ++i) + { + sal_uInt16 nSlot = 0; + switch (m_xStyleFamilies->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, *m_pParentDialog, *m_pBindings)); + } + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_WATERCAN, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_NEW_BY_EXAMPLE, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_UPDATE_BY_EXAMPLE, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_NEW, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_DRAGHIERARCHIE, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_EDIT, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_DELETE, *m_pParentDialog, *m_pBindings)); + pBoundItems[i++].reset( + new SfxTemplateControllerItem(SID_STYLE_FAMILY, *m_pParentDialog, *m_pBindings)); + m_pBindings->LEAVEREGISTRATIONS(); + + for (; i < COUNT_BOUND_FUNC; ++i) + pBoundItems[i] = nullptr; + + StartListening(*m_pBindings); + + for (i = SID_STYLE_FAMILY1; i <= SID_STYLE_FAMILY4; i++) + m_pBindings->Update(i); + + return nCount; +} + +void StyleList::EnableNewByExample(bool newByExampleDisabled) +{ + m_bNewByExampleDisabled = newByExampleDisabled; +} + +class TreeViewDropTarget final : public DropTargetHelper +{ +private: + StyleList& m_rParent; + +public: + TreeViewDropTarget(StyleList& rStyleList, weld::TreeView& rTreeView) + : DropTargetHelper(rTreeView.get_drop_target()) + , m_rParent(rStyleList) + { + } + + 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 StyleList::FilterSelect(sal_uInt16 nActFilter, bool bsetFilter) +{ + m_nActFilter = nActFilter; + if (bsetFilter) + { + SfxObjectShell* const pDocShell = m_aSaveSelection.Call(*this); + SfxStyleSheetBasePool* pOldStyleSheetPool = m_pStyleSheetPool; + m_pStyleSheetPool = pDocShell ? pDocShell->GetStyleSheetPool() : nullptr; + if (pOldStyleSheetPool != m_pStyleSheetPool) + { + if (pOldStyleSheetPool) + EndListening(*pOldStyleSheetPool); + if (m_pStyleSheetPool) + StartListening(*m_pStyleSheetPool); + } + } + UpdateStyles(StyleFlags::UpdateFamilyList); +} + +IMPL_LINK(StyleList, SetFamily, sal_uInt16, nId, void) +{ + if (m_nActFamily != 0xFFFF) + m_pParentDialog->CheckItem(OString::number(m_nActFamily), false); + m_nActFamily = nId; + if (nId != 0xFFFF) + { + m_bUpdateFamily = true; + } +} + +void StyleList::InvalidateBindings() +{ + m_pBindings->Invalidate(SID_STYLE_NEW_BY_EXAMPLE, true); + m_pBindings->Update(SID_STYLE_NEW_BY_EXAMPLE); + m_pBindings->Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE, true); + m_pBindings->Update(SID_STYLE_UPDATE_BY_EXAMPLE); + m_pBindings->Invalidate(SID_STYLE_WATERCAN, true); + m_pBindings->Update(SID_STYLE_WATERCAN); + m_pBindings->Invalidate(SID_STYLE_NEW, true); + m_pBindings->Update(SID_STYLE_NEW); + m_pBindings->Invalidate(SID_STYLE_DRAGHIERARCHIE, true); + m_pBindings->Update(SID_STYLE_DRAGHIERARCHIE); +} + +void StyleList::Initialize() +{ + m_pBindings->Invalidate(SID_STYLE_FAMILY); + m_pBindings->Update(SID_STYLE_FAMILY); + + m_xFmtLb->connect_row_activated(LINK(this, StyleList, TreeListApplyHdl)); + m_xFmtLb->connect_mouse_press(LINK(this, StyleList, MousePressHdl)); + m_xFmtLb->connect_query_tooltip(LINK(this, StyleList, QueryTooltipHdl)); + m_xFmtLb->connect_changed(LINK(this, StyleList, FmtSelectHdl)); + m_xFmtLb->connect_popup_menu(LINK(this, StyleList, PopupFlatMenuHdl)); + m_xFmtLb->connect_key_press(LINK(this, StyleList, KeyInputHdl)); + m_xFmtLb->set_selection_mode(SelectionMode::Multiple); + m_xTreeBox->connect_changed(LINK(this, StyleList, FmtSelectHdl)); + m_xTreeBox->connect_row_activated(LINK(this, StyleList, TreeListApplyHdl)); + m_xTreeBox->connect_mouse_press(LINK(this, StyleList, MousePressHdl)); + m_xTreeBox->connect_query_tooltip(LINK(this, StyleList, QueryTooltipHdl)); + m_xTreeBox->connect_popup_menu(LINK(this, StyleList, PopupTreeMenuHdl)); + m_xTreeBox->connect_key_press(LINK(this, StyleList, KeyInputHdl)); + m_xTreeBox->connect_drag_begin(LINK(this, StyleList, DragBeginHdl)); + m_xTreeView1DropTargetHelper.reset(new TreeViewDropTarget(*this, *m_xFmtLb)); + m_xTreeView2DropTargetHelper.reset(new TreeViewDropTarget(*this, *m_xTreeBox)); + + m_pParentDialog->connect_stylelist_read_resource(LINK(this, StyleList, ReadResource)); + m_pParentDialog->connect_stylelist_clear(LINK(this, StyleList, Clear)); + m_pParentDialog->connect_stylelist_cleanup(LINK(this, StyleList, Cleanup)); + m_pParentDialog->connect_stylelist_execute_drop(LINK(this, StyleList, ExecuteDrop)); + m_pParentDialog->connect_stylelist_execute_new_menu( + LINK(this, StyleList, NewMenuExecuteAction)); + m_pParentDialog->connect_stylelist_for_watercan(LINK(this, StyleList, IsSafeForWaterCan)); + m_pParentDialog->connect_stylelist_has_selected_style(LINK(this, StyleList, HasSelectedStyle)); + m_pParentDialog->connect_stylelist_update_style_dependents( + LINK(this, StyleList, UpdateStyleDependents)); + m_pParentDialog->connect_stylelist_enable_tree_drag(LINK(this, StyleList, EnableTreeDrag)); + m_pParentDialog->connect_stylelist_enable_delete(LINK(this, StyleList, EnableDelete)); + m_pParentDialog->connect_stylelist_set_water_can_state(LINK(this, StyleList, SetWaterCanState)); + m_pParentDialog->connect_set_family(LINK(this, StyleList, SetFamily)); + + int nTreeHeight = m_xFmtLb->get_height_rows(8); + m_xFmtLb->set_size_request(-1, nTreeHeight); + m_xTreeBox->set_size_request(-1, nTreeHeight); + + m_xFmtLb->connect_custom_get_size(LINK(this, StyleList, CustomGetSizeHdl)); + m_xFmtLb->connect_custom_render(LINK(this, StyleList, CustomRenderHdl)); + m_xTreeBox->connect_custom_get_size(LINK(this, StyleList, CustomGetSizeHdl)); + m_xTreeBox->connect_custom_render(LINK(this, StyleList, CustomRenderHdl)); + bool bCustomPreview = officecfg::Office::Common::StylesAndFormatting::Preview::get(); + m_xFmtLb->set_column_custom_renderer(0, bCustomPreview); + m_xTreeBox->set_column_custom_renderer(0, bCustomPreview); + + m_xFmtLb->set_visible(!m_bHierarchical); + m_xTreeBox->set_visible(m_bHierarchical); + Update(); +} + +void StyleList::UpdateFamily() +{ + m_bUpdateFamily = false; + + SfxDispatcher* pDispat = m_pBindings->GetDispatcher_Impl(); + SfxViewFrame* pViewFrame = pDispat->GetFrame(); + SfxObjectShell* pDocShell = pViewFrame->GetObjectShell(); + + SfxStyleSheetBasePool* pOldStyleSheetPool = m_pStyleSheetPool; + m_pStyleSheetPool = pDocShell ? pDocShell->GetStyleSheetPool() : nullptr; + if (pOldStyleSheetPool != m_pStyleSheetPool) + { + if (pOldStyleSheetPool) + EndListening(*pOldStyleSheetPool); + if (m_pStyleSheetPool) + StartListening(*m_pStyleSheetPool); + } + + m_bTreeDrag = true; + m_bCanNew = m_xTreeBox->get_visible() || m_xFmtLb->count_selected_rows() <= 1; + m_pParentDialog->EnableNew(m_bCanNew, this); + m_bTreeDrag = true; + if (m_pStyleSheetPool) + { + if (!m_xTreeBox->get_visible()) + UpdateStyles(StyleFlags::UpdateFamily | StyleFlags::UpdateFamilyList); + else + { + UpdateStyles(StyleFlags::UpdateFamily); + FillTreeBox(GetActualFamily()); + } + } + + InvalidateBindings(); +} + +bool StyleList::EnableExecute() +{ + return m_xTreeBox->get_visible() || m_xFmtLb->count_selected_rows() <= 1; +} + +void StyleList::connect_LoadFactoryStyleFilter(const Link<SfxObjectShell const*, sal_Int32>& rLink) +{ + m_aLoadFactoryStyleFilter = rLink; +} + +void StyleList::connect_SaveSelection(const Link<StyleList&, SfxObjectShell*> rLink) +{ + m_aSaveSelection = rLink; +} + +/** 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 StyleList::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 || m_bNewByExampleDisabled) + return DND_ACTION_NONE; + else + return DND_ACTION_COPY; + } + // to enable the autoscroll when we're close to the edges + weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); + pTreeView->get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true); + return DND_ACTION_MOVE; +} + +// handles drop of content in treeview when creating a new style +IMPL_LINK(StyleList, ExecuteDrop, const ExecuteDropEvent&, rEvt, sal_Int8) +{ + SfxObjectShell* pDocShell = m_pCurObjShell; + 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(m_pParentDialog, SfxCommonTemplateDialog_Impl, OnAsyncExecuteDrop), + this); + + bFormatFound = true; + nRet = rEvt.mnAction; + break; + } + } + } + + if (bFormatFound) + return nRet; + } + + if (!m_xTreeBox->get_visible()) + return DND_ACTION_NONE; + + if (!m_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 = m_xTreeBox->get_drag_source(); + // only dragging within the same widget allowed + if (!pSource || pSource != m_xTreeBox.get()) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xSource(m_xTreeBox->make_iterator()); + if (!m_xTreeBox->get_selected(xSource.get())) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xTarget(m_xTreeBox->make_iterator()); + if (!m_xTreeBox->get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true)) + { + // if nothing under the mouse, use the last row + int nChildren = m_xTreeBox->n_children(); + if (!nChildren) + return DND_ACTION_NONE; + if (!m_xTreeBox->get_iter_first(*xTarget) + || !m_xTreeBox->iter_nth_sibling(*xTarget, nChildren - 1)) + return DND_ACTION_NONE; + while (m_xTreeBox->get_row_expanded(*xTarget)) + { + nChildren = m_xTreeBox->iter_n_children(*xTarget); + if (!m_xTreeBox->iter_children(*xTarget) + || !m_xTreeBox->iter_nth_sibling(*xTarget, nChildren - 1)) + return DND_ACTION_NONE; + } + } + OUString aTargetStyle = m_xTreeBox->get_text(*xTarget); + DropHdl(m_xTreeBox->get_text(*xSource), aTargetStyle); + m_xTreeBox->unset_drag_dest_row(); + FillTreeBox(GetActualFamily()); + m_pParentDialog->SelectStyle(aTargetStyle, false, *this); + return DND_ACTION_NONE; +} + +IMPL_LINK_NOARG(StyleList, NewMenuExecuteAction, void*, void) +{ + if (!m_pStyleSheetPool || m_nActFamily == 0xffff) + return; + + const SfxStyleFamily eFam = GetFamilyItem()->GetFamily(); + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + SfxStyleSearchBits nFilter(SfxStyleSearchBits::Auto); + if (pItem && m_nActFilter != 0xffff) + nFilter = pItem->GetFilterList()[m_nActFilter].nFlags; + if (nFilter == SfxStyleSearchBits::Auto) // automatic + nFilter = m_nAppFilter; + + // why? : FloatingWindow must not be parent of a modal dialog + SfxNewStyleDlg aDlg(m_pContainer, *m_pStyleSheetPool, eFam); + auto nResult = aDlg.run(); + if (nResult == RET_OK) + { + const OUString aTemplName(aDlg.GetName()); + m_pParentDialog->Execute_Impl(SID_STYLE_NEW_BY_EXAMPLE, aTemplName, "", + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this, + nFilter); + UpdateFamily(); + m_aUpdateFamily.Call(*this); + } +} + +void StyleList::DropHdl(const OUString& rStyle, const OUString& rParent) +{ + m_bDontUpdate = true; + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + const SfxStyleFamily eFam = pItem->GetFamily(); + m_pStyleSheetPool->SetParent(eFam, rStyle, rParent); + m_bDontUpdate = false; +} + +void StyleList::PrepareMenu(const Point& rPos) +{ + weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.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); +} + +/** 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, std::u16string_view 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, + const 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, 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; + } +} +} + +sal_uInt16 StyleList::StyleNrToInfoOffset(sal_uInt16 nId) +{ + const SfxStyleFamilyItem& rItem = m_xStyleFamilies->at(nId); + return SfxTemplate::SfxFamilyIdToNId(rItem.GetFamily()) - 1; +} + +// Helper function: Access to the current family item +const SfxStyleFamilyItem* StyleList::GetFamilyItem() const +{ + const size_t nCount = m_xStyleFamilies->size(); + for (size_t i = 0; i < nCount; ++i) + { + const SfxStyleFamilyItem& rItem = m_xStyleFamilies->at(i); + sal_uInt16 nId = SfxTemplate::SfxFamilyIdToNId(rItem.GetFamily()); + if (nId == m_nActFamily) + return &rItem; + } + return nullptr; +} + +void StyleList::GetSelectedStyle() const +{ + const OUString aTemplName(GetSelectedEntry()); + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + m_pStyleSheetPool->Find(aTemplName, pItem->GetFamily()); +} + +// Used to get the current selected entry in visible treeview +OUString StyleList::GetSelectedEntry() const +{ + OUString aRet; + if (m_xTreeBox->get_visible()) + aRet = m_xTreeBox->get_selected_text(); + else + aRet = m_xFmtLb->get_selected_text(); + return aRet; +} + +/** + * 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 m_xTreeBox showing or an m_xFmtLb (which we hide when not shown) + */ +IMPL_LINK_NOARG(StyleList, IsSafeForWaterCan, void*, bool) +{ + if (m_xTreeBox->get_visible()) + return m_xTreeBox->get_selected_index() != -1; + else + return m_xFmtLb->count_selected_rows() == 1; +} + +IMPL_LINK(StyleList, SetWaterCanState, const SfxBoolItem*, pItem, void) +{ + size_t nCount = m_xStyleFamilies->size(); + m_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(); + } + } + m_pBindings->LeaveRegistrations(); +} + +void StyleList::FamilySelect(sal_uInt16 nEntry) +{ + m_nActFamily = nEntry; + SfxDispatcher* pDispat = m_pBindings->GetDispatcher_Impl(); + SfxUInt16Item const aItem(SID_STYLE_FAMILY, + static_cast<sal_uInt16>(SfxTemplate::NIdToSfxFamilyId(nEntry))); + pDispat->ExecuteList(SID_STYLE_FAMILY, SfxCallMode::SYNCHRON, { &aItem }); + m_pBindings->Invalidate(SID_STYLE_FAMILY); + m_pBindings->Update(SID_STYLE_FAMILY); + UpdateFamily(); + m_aUpdateFamily.Call(*this); +} + +// It selects the style in treeview +// bIsCallBack is true for the selected style. For eg. if "Addressee" is selected in +// styles, bIsCallBack will be true for it. +void StyleList::SelectStyle(const OUString& rStr, bool bIsCallback) +{ + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + if (!pItem) + return; + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSheetBase* pStyle = m_pStyleSheetPool->Find(rStr, eFam); + if (pStyle) + { + bool bReadWrite = !(pStyle->GetMask() & SfxStyleSearchBits::ReadOnly); + m_pParentDialog->EnableEdit(bReadWrite, this); + m_pParentDialog->EnableHide(bReadWrite && !pStyle->IsHidden() && !pStyle->IsUsed(), this); + m_pParentDialog->EnableShow(bReadWrite && pStyle->IsHidden(), this); + } + else + { + m_pParentDialog->EnableEdit(false, this); + m_pParentDialog->EnableHide(false, this); + m_pParentDialog->EnableShow(false, this); + } + + if (bIsCallback) + return; + + if (m_xTreeBox->get_visible()) + { + if (!rStr.isEmpty()) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeBox->make_iterator(); + bool bEntry = m_xTreeBox->get_iter_first(*xEntry); + while (bEntry) + { + if (m_xTreeBox->get_text(*xEntry) == rStr) + { + m_xTreeBox->scroll_to_row(*xEntry); + m_xTreeBox->select(*xEntry); + break; + } + bEntry = m_xTreeBox->iter_next(*xEntry); + } + } + else if (eFam == SfxStyleFamily::Pseudo) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeBox->make_iterator(); + if (m_xTreeBox->get_iter_first(*xEntry)) + { + m_xTreeBox->scroll_to_row(*xEntry); + m_xTreeBox->select(*xEntry); + } + } + else + m_xTreeBox->unselect_all(); + } + else + { + bool bSelect = !rStr.isEmpty(); + if (bSelect) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xFmtLb->make_iterator(); + bool bEntry = m_xFmtLb->get_iter_first(*xEntry); + while (bEntry && m_xFmtLb->get_text(*xEntry) != rStr) + bEntry = m_xFmtLb->iter_next(*xEntry); + if (!bEntry) + bSelect = false; + else + { + if (!m_xFmtLb->is_selected(*xEntry)) + { + m_xFmtLb->unselect_all(); + m_xFmtLb->scroll_to_row(*xEntry); + m_xFmtLb->select(*xEntry); + } + } + } + + if (!bSelect) + { + m_xFmtLb->unselect_all(); + m_pParentDialog->EnableEdit(false, this); + m_pParentDialog->EnableHide(false, this); + m_pParentDialog->EnableShow(false, this); + } + } +} + +static void MakeExpanded_Impl(const 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)); + } +} + +IMPL_LINK(StyleList, EnableTreeDrag, bool, m_bEnable, void) +{ + if (m_pStyleSheetPool) + { + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + SfxStyleSheetBase* pStyle = pItem ? m_pStyleSheetPool->First(pItem->GetFamily()) : nullptr; + m_bAllowReParentDrop = pStyle && pStyle->HasParentSupport() && m_bEnable; + } + m_bTreeDrag = m_bEnable; +} + +// Fill the treeview + +void StyleList::FillTreeBox(SfxStyleFamily eFam) +{ + assert(m_xTreeBox && "FillTreeBox() without treebox"); + if (!m_pStyleSheetPool || m_nActFamily == 0xffff) + return; + + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + if (!pItem) + return; + + StyleTreeArr_Impl aArr; + SfxStyleSheetBase* pStyle = m_pStyleSheetPool->First(eFam, SfxStyleSearchBits::AllVisible); + + m_bAllowReParentDrop = pStyle && pStyle->HasParentSupport() && m_bTreeDrag; + + while (pStyle) + { + StyleTree_Impl* pNew = new StyleTree_Impl(pStyle->GetName(), pStyle->GetParent()); + aArr.emplace_back(pNew); + pStyle = m_pStyleSheetPool->Next(); + } + OUString aUIName = getDefaultStyleName(eFam); + MakeTree_Impl(aArr, aUIName); + std::vector<OUString> aEntries; + MakeExpanded_Impl(*m_xTreeBox, aEntries); + m_xTreeBox->freeze(); + m_xTreeBox->clear(); + const sal_uInt16 nCount = aArr.size(); + + for (sal_uInt16 i = 0; i < nCount; ++i) + { + FillBox_Impl(*m_xTreeBox, aArr[i].get(), aEntries, eFam, nullptr); + aArr[i].reset(); + } + + m_pParentDialog->EnableItem("watercan", false); + + SfxTemplateItem* pState = m_pFamilyState[m_nActFamily - 1].get(); + + m_xTreeBox->thaw(); + + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeBox->make_iterator(); + bool bEntry = m_xTreeBox->get_iter_first(*xEntry); + if (bEntry && nCount) + m_xTreeBox->expand_row(*xEntry); + + while (bEntry) + { + if (IsExpanded_Impl(aEntries, m_xTreeBox->get_text(*xEntry))) + m_xTreeBox->expand_row(*xEntry); + bEntry = m_xTreeBox->iter_next(*xEntry); + } + + OUString aStyle; + if (pState) // Select current entry + aStyle = pState->GetStyleName(); + m_pParentDialog->SelectStyle(aStyle, false, *this); + EnableDelete(nullptr); +} + +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"; + if (nFamily == SfxStyleFamily::Pseudo) + return "NumberingStyles"; + return OUString(); +} + +OUString StyleList::getDefaultStyleName(const SfxStyleFamily eFam) +{ + OUString sDefaultStyle; + OUString aFamilyName = lcl_GetStyleFamilyName(eFam); + if (aFamilyName == "TableStyles") + sDefaultStyle = "Default Style"; + else if (aFamilyName == "NumberingStyles") + sDefaultStyle = "No List"; + else + sDefaultStyle = "Standard"; + uno::Reference<style::XStyleFamiliesSupplier> xModel(m_pCurObjShell->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; +} + +SfxStyleFamily StyleList::GetActualFamily() const +{ + const SfxStyleFamilyItem* pFamilyItem = GetFamilyItem(); + if (!pFamilyItem || m_nActFamily == 0xffff) + return SfxStyleFamily::Para; + else + return pFamilyItem->GetFamily(); +} + +IMPL_LINK_NOARG(StyleList, HasSelectedStyle, void*, bool) +{ + return m_xTreeBox->get_visible() ? m_xTreeBox->get_selected_index() != -1 + : m_xFmtLb->count_selected_rows() != 0; +} + +IMPL_LINK_NOARG(StyleList, UpdateStyleDependents, void*, void) +{ + // Trigger Help PI. Only when the watercan is on + if (m_nActFamily != 0xffff && m_pParentDialog->IsCheckedItem("watercan") && + // only if that region is allowed + nullptr != m_pFamilyState[m_nActFamily - 1] && IsSafeForWaterCan(nullptr)) + { + m_pParentDialog->Execute_Impl(SID_STYLE_WATERCAN, "", "", 0, *this); + m_pParentDialog->Execute_Impl(SID_STYLE_WATERCAN, GetSelectedEntry(), "", + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this); + } +} + +// Comes into action when the current style is changed +void StyleList::UpdateStyles(StyleFlags nFlags) +{ + OSL_ENSURE(nFlags != StyleFlags::NONE, "nothing to do"); + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + if (!pItem) + { + // Is the case for the template catalog + const size_t nFamilyCount = m_xStyleFamilies->size(); + size_t n; + for (n = 0; n < nFamilyCount; n++) + if (m_pFamilyState[StyleNrToInfoOffset(n)]) + break; + if (n == nFamilyCount) + // It happens sometimes, God knows why + return; + m_nAppFilter = m_pFamilyState[StyleNrToInfoOffset(n)]->GetValue(); + m_pParentDialog->FamilySelect(StyleNrToInfoOffset(n) + 1, *this); + pItem = GetFamilyItem(); + } + + const SfxStyleFamily eFam = pItem->GetFamily(); + + SfxStyleSearchBits nFilter(m_nActFilter < pItem->GetFilterList().size() + ? pItem->GetFilterList()[m_nActFilter].nFlags + : SfxStyleSearchBits::Auto); + if (nFilter == SfxStyleSearchBits::Auto) // automatic + nFilter = m_nAppFilter; + + OSL_ENSURE(m_pStyleSheetPool, "no StyleSheetPool"); + if (!m_pStyleSheetPool) + return; + + pItem = GetFamilyItem(); + + m_aUpdateStyles.Call(nFlags); + + SfxStyleSheetBase* pStyle = m_pStyleSheetPool->First(eFam, nFilter); + + std::unique_ptr<weld::TreeIter> xEntry = m_xFmtLb->make_iterator(); + bool bEntry = m_xFmtLb->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 = m_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] == m_xFmtLb->get_text(*xEntry)) + { + ++nPos; + bEntry = m_xFmtLb->iter_next(*xEntry); + } + + if (nPos < nCount || bEntry) + { + // Fills the display box + m_xFmtLb->freeze(); + m_xFmtLb->clear(); + + for (nPos = 0; nPos < nCount; ++nPos) + m_xFmtLb->append(aStrings[nPos], aStrings[nPos]); + + m_xFmtLb->thaw(); + } + // Selects the current style if any + SfxTemplateItem* pState = m_pFamilyState[m_nActFamily - 1].get(); + OUString aStyle; + if (pState) + aStyle = pState->GetStyleName(); + m_pParentDialog->SelectStyle(aStyle, false, *this); + EnableDelete(nullptr); +} + +void StyleList::SetFamilyState(sal_uInt16 nSlotId, const SfxTemplateItem* pItem) +{ + sal_uInt16 nIdx = nSlotId - SID_STYLE_FAMILY_START; + m_pFamilyState[nIdx].reset(); + if (pItem) + m_pFamilyState[nIdx].reset(new SfxTemplateItem(*pItem)); + m_bUpdateFamily = true; +} + +void StyleList::SetHierarchical() +{ + m_bHierarchical = true; + const OUString aSelectEntry(GetSelectedEntry()); + m_xFmtLb->hide(); + FillTreeBox(GetActualFamily()); + m_pParentDialog->SelectStyle(aSelectEntry, false, *this); + m_xTreeBox->show(); +} + +void StyleList::SetFilterControlsHandle() +{ + m_xTreeBox->hide(); + m_xFmtLb->show(); + m_bHierarchical = false; +} + +// Handler for the New-Buttons +void StyleList::NewHdl() +{ + if (m_nActFamily == 0xffff + || !(m_xTreeBox->get_visible() || m_xFmtLb->count_selected_rows() <= 1)) + return; + + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSearchBits nMask(SfxStyleSearchBits::Auto); + if (m_nActFilter != 0xffff) + nMask = pItem->GetFilterList()[m_nActFilter].nFlags; + if (nMask == SfxStyleSearchBits::Auto) // automatic + nMask = m_nAppFilter; + + m_pParentDialog->Execute_Impl(SID_STYLE_NEW, "", GetSelectedEntry(), + static_cast<sal_uInt16>(eFam), *this, nMask); +} + +// Handler for the edit-Buttons +void StyleList::EditHdl() +{ + if (m_nActFamily != 0xffff && HasSelectedStyle(nullptr)) + { + sal_uInt16 nFilter = m_nActFilter; + OUString aTemplName(GetSelectedEntry()); + GetSelectedStyle(); // -Wall required?? + m_pParentDialog->Execute_Impl(SID_STYLE_EDIT, aTemplName, OUString(), + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this, + SfxStyleSearchBits::Auto, &nFilter); + } +} + +// Handler for the Delete-Buttons +void StyleList::DeleteHdl() +{ + if (m_nActFamily == 0xffff || !HasSelectedStyle(nullptr)) + 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 = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + + OUStringBuffer aMsg; + aMsg.append(SfxResId(STR_DELETE_STYLE_USED) + 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 = m_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)); + m_bDontUpdate = true; // To prevent the Treelistbox to shut down while deleting + m_pParentDialog->Execute_Impl(SID_STYLE_DELETE, aTemplName, OUString(), + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this); + + if (m_xTreeBox->get_visible()) + { + weld::RemoveParentKeepChildren(*m_xTreeBox, *elem); + m_bDontUpdate = false; + } + } + m_bDontUpdate = false; // if everything is deleted set m_bDontUpdate back to false + UpdateStyles(StyleFlags::UpdateFamilyList); // and force-update the list +} + +void StyleList::HideHdl() +{ + if (m_nActFamily == 0xffff || !HasSelectedStyle(nullptr)) + return; + + weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); + pTreeView->selected_foreach([this, pTreeView](weld::TreeIter& rEntry) { + OUString aTemplName = pTreeView->get_text(rEntry); + + m_pParentDialog->Execute_Impl(SID_STYLE_HIDE, aTemplName, OUString(), + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this); + + return false; + }); +} + +void StyleList::ShowHdl() +{ + if (m_nActFamily == 0xffff || !HasSelectedStyle(nullptr)) + return; + + weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); + pTreeView->selected_foreach([this, pTreeView](weld::TreeIter& rEntry) { + OUString aTemplName = pTreeView->get_text(rEntry); + + m_pParentDialog->Execute_Impl(SID_STYLE_SHOW, aTemplName, OUString(), + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this); + + return false; + }); +} + +IMPL_LINK_NOARG(StyleList, EnableDelete, void*, void) +{ + bool bEnableDelete(false); + if (m_nActFamily != 0xffff && HasSelectedStyle(nullptr)) + { + OSL_ENSURE(m_pStyleSheetPool, "No StyleSheetPool"); + const OUString aTemplName(GetSelectedEntry()); + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSearchBits nFilter = SfxStyleSearchBits::Auto; + if (pItem->GetFilterList().size() > m_nActFilter) + nFilter = pItem->GetFilterList()[m_nActFilter].nFlags; + if (nFilter == SfxStyleSearchBits::Auto) // automatic + nFilter = m_nAppFilter; + const SfxStyleSheetBase* pStyle = m_pStyleSheetPool->Find( + aTemplName, eFam, m_xTreeBox->get_visible() ? SfxStyleSearchBits::All : nFilter); + + OSL_ENSURE(pStyle, "Style not found"); + if (pStyle && pStyle->IsUserDefined()) + { + if (pStyle->HasClearParentSupport() || !pStyle->IsUsed()) + { + bEnableDelete = true; + } + } + } + m_pParentDialog->EnableDel(bEnableDelete, this); +} + +IMPL_LINK_NOARG(StyleList, Clear, void*, void) +{ + m_xStyleFamilies.reset(); + for (auto& i : m_pFamilyState) + i.reset(); + m_pCurObjShell = nullptr; + for (auto& i : pBoundItems) + i.reset(); +} + +void StyleList::ShowMenu(const CommandEvent& rCEvt) +{ + CreateContextMenu(); + weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); + OString sCommand( + mxMenu->popup_at_rect(pTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1)))); + MenuSelect(sCommand); +} + +void StyleList::MenuSelect(const OString& rIdent) +{ + sLastItemIdent = rIdent; + if (sLastItemIdent.isEmpty()) + return; + Application::PostUserEvent(LINK(this, StyleList, MenuSelectAsyncHdl)); /***check this****/ +} + +void StyleList::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) +{ + const SfxHintId nId = rHint.GetId(); + + switch (nId) + { + case SfxHintId::UpdateDone: + { + SfxViewFrame* pViewFrame = m_pBindings->GetDispatcher_Impl()->GetFrame(); + SfxObjectShell* pDocShell = pViewFrame->GetObjectShell(); + if (m_pParentDialog->GetNotifyUpdate() + && (!m_pParentDialog->IsCheckedItem("watercan") + || (pDocShell && pDocShell->GetStyleSheetPool() != m_pStyleSheetPool))) + { + m_pParentDialog->SetNotifyupdate(false); + Update(); + } + else if (m_bUpdateFamily) + { + UpdateFamily(); + m_aUpdateFamily.Call(*this); + } + + if (m_pStyleSheetPool) + { + OUString aStr = GetSelectedEntry(); + if (!aStr.isEmpty()) + { + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + if (!pItem) + break; + const SfxStyleFamily eFam = pItem->GetFamily(); + SfxStyleSheetBase* pStyle = m_pStyleSheetPool->Find(aStr, eFam); + if (pStyle) + { + bool bReadWrite = !(pStyle->GetMask() & SfxStyleSearchBits::ReadOnly); + m_pParentDialog->EnableEdit(bReadWrite, this); + m_pParentDialog->EnableHide( + bReadWrite && !pStyle->IsUsed() && !pStyle->IsHidden(), this); + m_pParentDialog->EnableShow(bReadWrite && pStyle->IsHidden(), this); + } + else + { + m_pParentDialog->EnableEdit(false, this); + m_pParentDialog->EnableHide(false, this); + m_pParentDialog->EnableShow(false, this); + } + } + } + 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: + m_pParentDialog->SetNotifyupdate(true); + break; + case SfxHintId::Dying: + { + EndListening(*m_pStyleSheetPool); + m_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 (!m_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, StyleList, TimeOut)); + } + pIdle->Start(); + } +} + +IMPL_LINK_NOARG(StyleList, TimeOut, Timer*, void) +{ + if (!m_bDontUpdate) + { + m_bDontUpdate = true; + if (!m_xTreeBox->get_visible()) + UpdateStyles(StyleFlags::UpdateFamilyList); + else + { + FillTreeBox(GetActualFamily()); + SfxTemplateItem* pState = m_pFamilyState[m_nActFamily - 1].get(); + if (pState) + { + m_pParentDialog->SelectStyle(pState->GetStyleName(), false, *this); + EnableDelete(nullptr); + } + } + m_bDontUpdate = false; + pIdle.reset(); + } + else + pIdle->Start(); +} + +IMPL_LINK_NOARG(StyleList, 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(); +} + +// Double-click on a style sheet in the ListBox is applied. +IMPL_LINK(StyleList, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + // Allow normal processing. only if bAllowReParentDrop is true + return !m_bAllowReParentDrop; +} + +IMPL_LINK(StyleList, KeyInputHdl, const KeyEvent&, rKeyEvent, bool) +{ + bool bRet = false; + const vcl::KeyCode& rKeyCode = rKeyEvent.GetKeyCode(); + if (m_bCanDel && !rKeyCode.GetModifier() && rKeyCode.GetCode() == KEY_DELETE) + { + DeleteHdl(); + bRet = true; + } + return bRet; +} + +IMPL_LINK(StyleList, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString) +{ + weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); + const OUString aTemplName(pTreeView->get_text(rEntry)); + OUString sQuickHelpText(aTemplName); + + const SfxStyleFamilyItem* pItem = GetFamilyItem(); + if (!pItem) + return sQuickHelpText; + SfxStyleSheetBase* pStyle = m_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 = OUString::Concat(sUsedBy.subView(0, nMaxLen)) + "..."; + } + + OUString aMessage = SfxResId(STR_STYLEUSEDBY); + aMessage = aMessage.replaceFirst("%STYLELIST", sUsedBy); + sQuickHelpText = aTemplName + " " + aMessage; + } + } + + return sQuickHelpText; +} + +IMPL_LINK(StyleList, 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(vcl::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) + { + if (const SfxStyleFamilyItem* pItem = GetFamilyItem()) + { + SfxStyleSheetBase* pStyleSheet = pStyleManager->Search(rId, pItem->GetFamily()); + + if (pStyleSheet) + { + rRenderContext.Push(vcl::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(); +} + +// Selection of a template during the Watercan-Status +IMPL_LINK(StyleList, FmtSelectHdl, weld::TreeView&, rListBox, void) +{ + std::unique_ptr<weld::TreeIter> xHdlEntry = rListBox.make_iterator(); + if (!rListBox.get_cursor(xHdlEntry.get())) + return; + + m_pParentDialog->SelectStyle(rListBox.get_text(*xHdlEntry), true, *this); +} + +IMPL_LINK_NOARG(StyleList, TreeListApplyHdl, weld::TreeView&, bool) +{ + // only if that region is allowed + if (m_nActFamily != 0xffff && nullptr != m_pFamilyState[m_nActFamily - 1] + && !GetSelectedEntry().isEmpty()) + { + m_pParentDialog->Execute_Impl(SID_STYLE_APPLY, GetSelectedEntry(), OUString(), + static_cast<sal_uInt16>(GetFamilyItem()->GetFamily()), *this, + SfxStyleSearchBits::Auto, nullptr, &m_nModifier); + } + // After selecting a focused item if possible again on the app window + if (dynamic_cast<const SfxTemplateDialog_Impl*>(m_pParentDialog) != nullptr) + { + SfxViewFrame* pViewFrame = m_pBindings->GetDispatcher_Impl()->GetFrame(); + SfxViewShell* pVu = pViewFrame->GetViewShell(); + vcl::Window* pAppWin = pVu ? pVu->GetWindow() : nullptr; + if (pAppWin) + pAppWin->GrabFocus(); + } + + return true; +} + +IMPL_LINK(StyleList, MousePressHdl, const MouseEvent&, rMEvt, bool) +{ + m_nModifier = rMEvt.GetModifier(); + return false; +} + +// Notice from SfxBindings that the update is completed. Pushes out the update +// of the display. +void StyleList::Update() +{ + bool bDocChanged = false; + SfxStyleSheetBasePool* pNewPool = nullptr; + SfxViewFrame* pViewFrame = m_pBindings->GetDispatcher_Impl()->GetFrame(); + SfxObjectShell* pDocShell = pViewFrame->GetObjectShell(); + if (pDocShell) + pNewPool = pDocShell->GetStyleSheetPool(); + + if (pNewPool != m_pStyleSheetPool && pDocShell) + { + SfxModule* pNewModule = pDocShell->GetModule(); + if (pNewModule && pNewModule != m_Module) + { + m_aClearResource.Call(nullptr); + m_aReadResource.Call(*this); + } + if (m_pStyleSheetPool) + { + EndListening(*m_pStyleSheetPool); + m_pStyleSheetPool = nullptr; + } + + if (pNewPool) + { + StartListening(*pNewPool); + m_pStyleSheetPool = pNewPool; + bDocChanged = true; + } + } + + if (m_bUpdateFamily) + { + UpdateFamily(); + m_aUpdateFamily.Call(*this); + } + + sal_uInt16 i; + for (i = 0; i < MAX_FAMILIES; ++i) + if (m_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 (m_nActFamily == 0xffff || nullptr == (pItem = m_pFamilyState[m_nActFamily - 1].get())) + { + m_pParentDialog->CheckItem(OString::number(m_nActFamily), false); + const size_t nFamilyCount = m_xStyleFamilies->size(); + size_t n; + for (n = 0; n < nFamilyCount; n++) + if (m_pFamilyState[StyleNrToInfoOffset(n)]) + break; + + std::unique_ptr<SfxTemplateItem>& pNewItem = m_pFamilyState[StyleNrToInfoOffset(n)]; + m_nAppFilter = pNewItem->GetValue(); + m_pParentDialog->FamilySelect(StyleNrToInfoOffset(n) + 1, *this); + pItem = pNewItem.get(); + } + else if (bDocChanged) + { + // other DocShell -> all new + m_pParentDialog->CheckItem(OString::number(m_nActFamily)); + m_nActFilter = static_cast<sal_uInt16>(m_aLoadFactoryStyleFilter.Call(pDocShell)); + m_pParentDialog->IsUpdate(*this); + if (0xffff == m_nActFilter) + { + m_nActFilter = pDocShell->GetAutoStyleFilterIndex(); + } + + m_nAppFilter = pItem->GetValue(); + if (!m_xTreeBox->get_visible()) + { + UpdateStyles(StyleFlags::UpdateFamilyList); + } + else + FillTreeBox(GetActualFamily()); + } + else + { + // other filters for automatic + m_pParentDialog->CheckItem(OString::number(m_nActFamily)); + const SfxStyleFamilyItem* pStyleItem = GetFamilyItem(); + if (pStyleItem + && SfxStyleSearchBits::Auto == pStyleItem->GetFilterList()[m_nActFilter].nFlags + && m_nAppFilter != pItem->GetValue()) + { + m_nAppFilter = pItem->GetValue(); + if (!m_xTreeBox->get_visible()) + UpdateStyles(StyleFlags::UpdateFamilyList); + else + FillTreeBox(GetActualFamily()); + } + else + { + m_nAppFilter = pItem->GetValue(); + } + } + const OUString aStyle(pItem->GetStyleName()); + m_pParentDialog->SelectStyle(aStyle, false, *this); + EnableDelete(nullptr); + m_pParentDialog->EnableNew(m_bCanNew, this); +} + +void StyleList::EnablePreview(bool bCustomPreview) +{ + m_xFmtLb->clear(); + m_xFmtLb->set_column_custom_renderer(0, bCustomPreview); + m_xTreeBox->clear(); + m_xTreeBox->set_column_custom_renderer(0, bCustomPreview); +} + +const SfxStyleFamilyItem& StyleList::GetFamilyItemByIndex(size_t i) const +{ + return m_xStyleFamilies->at(i); +} + +IMPL_STATIC_LINK(StyleList, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size) +{ + vcl::RenderContext& rRenderContext = aPayload.first; + return Size(42, 32 * rRenderContext.GetDPIScaleFactor()); +} + +IMPL_LINK(StyleList, PopupFlatMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + PrepareMenu(rCEvt.GetMousePosPixel()); + + if (m_xFmtLb->count_selected_rows() <= 0) + { + m_pParentDialog->EnableEdit(false, this); + m_pParentDialog->EnableDel(false, this); + } + + ShowMenu(rCEvt); + + return true; +} + +IMPL_LINK(StyleList, PopupTreeMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + PrepareMenu(rCEvt.GetMousePosPixel()); + + ShowMenu(rCEvt); + + return true; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |