summaryrefslogtreecommitdiffstats
path: root/sfx2/source/dialog
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/dialog')
-rw-r--r--sfx2/source/dialog/StyleList.cxx1778
-rw-r--r--sfx2/source/dialog/alienwarn.cxx79
-rw-r--r--sfx2/source/dialog/backingcomp.cxx736
-rw-r--r--sfx2/source/dialog/backingwindow.cxx780
-rw-r--r--sfx2/source/dialog/backingwindow.hxx125
-rw-r--r--sfx2/source/dialog/basedlgs.cxx329
-rw-r--r--sfx2/source/dialog/bluthsnd.cxx50
-rw-r--r--sfx2/source/dialog/charmappopup.cxx73
-rw-r--r--sfx2/source/dialog/checkin.cxx40
-rw-r--r--sfx2/source/dialog/dialoghelper.cxx48
-rw-r--r--sfx2/source/dialog/dinfdlg.cxx2446
-rw-r--r--sfx2/source/dialog/dockwin.cxx1543
-rw-r--r--sfx2/source/dialog/documentfontsdialog.cxx113
-rw-r--r--sfx2/source/dialog/filedlghelper.cxx3000
-rw-r--r--sfx2/source/dialog/filedlgimpl.hxx220
-rw-r--r--sfx2/source/dialog/filtergrouping.cxx1169
-rw-r--r--sfx2/source/dialog/filtergrouping.hxx96
-rw-r--r--sfx2/source/dialog/infobar.cxx524
-rw-r--r--sfx2/source/dialog/inputdlg.cxx67
-rw-r--r--sfx2/source/dialog/mailmodel.cxx849
-rw-r--r--sfx2/source/dialog/mgetempl.cxx653
-rw-r--r--sfx2/source/dialog/mgetempl.hxx95
-rw-r--r--sfx2/source/dialog/navigat.cxx53
-rw-r--r--sfx2/source/dialog/newstyle.cxx92
-rw-r--r--sfx2/source/dialog/partwnd.cxx174
-rw-r--r--sfx2/source/dialog/passwd.cxx209
-rw-r--r--sfx2/source/dialog/printopt.cxx290
-rw-r--r--sfx2/source/dialog/recfloat.cxx145
-rw-r--r--sfx2/source/dialog/securitypage.cxx451
-rw-r--r--sfx2/source/dialog/securitypage.hxx43
-rw-r--r--sfx2/source/dialog/sfxdlg.cxx29
-rw-r--r--sfx2/source/dialog/splitwin.cxx1155
-rw-r--r--sfx2/source/dialog/srchdlg.cxx135
-rw-r--r--sfx2/source/dialog/styfitem.cxx35
-rw-r--r--sfx2/source/dialog/styledlg.cxx124
-rw-r--r--sfx2/source/dialog/tabdlg.cxx1160
-rw-r--r--sfx2/source/dialog/templdlg.cxx909
-rw-r--r--sfx2/source/dialog/tplcitem.cxx169
-rw-r--r--sfx2/source/dialog/tplpitem.cxx90
-rw-r--r--sfx2/source/dialog/versdlg.cxx473
40 files changed, 20549 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: */
diff --git a/sfx2/source/dialog/alienwarn.cxx b/sfx2/source/dialog/alienwarn.cxx
new file mode 100644
index 000000000..15fe92ccd
--- /dev/null
+++ b/sfx2/source/dialog/alienwarn.cxx
@@ -0,0 +1,79 @@
+/* -*- 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 <alienwarn.hxx>
+#include <officecfg/Office/Common.hxx>
+
+SfxAlienWarningDialog::SfxAlienWarningDialog(weld::Window* pParent,
+ std::u16string_view _rFormatName,
+ const OUString& _rDefaultExtension,
+ bool rDefaultIsAlien)
+ : MessageDialogController(pParent, "sfx/ui/alienwarndialog.ui", "AlienWarnDialog", "ask")
+ , m_xKeepCurrentBtn(m_xBuilder->weld_button("save"))
+ , m_xUseDefaultFormatBtn(m_xBuilder->weld_button("cancel"))
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+{
+ OUString aExtension = "ODF";
+
+ // replace formatname (text)
+ OUString sInfoText = m_xDialog->get_primary_text();
+ sInfoText = sInfoText.replaceAll("%FORMATNAME", _rFormatName);
+ m_xDialog->set_primary_text(sInfoText);
+
+ // replace formatname (button)
+ sInfoText = m_xKeepCurrentBtn->get_label();
+ sInfoText = sInfoText.replaceAll("%FORMATNAME", _rFormatName);
+ m_xKeepCurrentBtn->set_label(sInfoText);
+
+ // hide ODF explanation if default format is alien
+ // and set the proper extension in the button
+ if (rDefaultIsAlien)
+ {
+ m_xDialog->set_secondary_text(OUString());
+ aExtension = _rDefaultExtension.toAsciiUpperCase();
+ }
+
+ // replace defaultextension (button)
+ sInfoText = m_xUseDefaultFormatBtn->get_label();
+ sInfoText = sInfoText.replaceAll("%DEFAULTEXTENSION", aExtension);
+ m_xUseDefaultFormatBtn->set_label(sInfoText);
+
+ // load value of "warning on" checkbox from save options
+ m_xWarningOnBox->set_active(officecfg::Office::Common::Save::Document::WarnAlienFormat::get());
+}
+
+SfxAlienWarningDialog::~SfxAlienWarningDialog()
+{
+ try
+ {
+ // save value of "warning off" checkbox, if necessary
+ bool bChecked = m_xWarningOnBox->get_active();
+ if (officecfg::Office::Common::Save::Document::WarnAlienFormat::get() != bChecked)
+ {
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Save::Document::WarnAlienFormat::set(bChecked, xChanges);
+ xChanges->commit();
+ }
+ }
+ catch (...)
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/backingcomp.cxx b/sfx2/source/dialog/backingcomp.cxx
new file mode 100644
index 000000000..845435ddc
--- /dev/null
+++ b/sfx2/source/dialog/backingcomp.cxx
@@ -0,0 +1,736 @@
+/* -*- 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 "backingwindow.hxx"
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/KeyEvent.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XKeyListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/weak.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+
+namespace {
+
+/**
+ implements the backing component.
+
+ This component is a special one, which doesn't provide a controller
+ nor a model. It supports the following features:
+ - Drag & Drop
+ - Key Accelerators
+ - Simple Menu
+ - Progress Bar
+ - Background
+ */
+class BackingComp : public css::lang::XTypeProvider
+ , public css::lang::XServiceInfo
+ , public css::lang::XInitialization
+ , public css::frame::XController // => XComponent
+ , public css::awt::XKeyListener // => XEventListener
+ , public css::frame::XDispatchProvider
+ , public css::frame::XDispatch
+ , public ::cppu::OWeakObject
+{
+private:
+ /** reference to the component window. */
+ css::uno::Reference< css::awt::XWindow > m_xWindow;
+
+ /** the owner frame of this component. */
+ css::uno::Reference< css::frame::XFrame > m_xFrame;
+
+ Size m_aInitialWindowMinSize;
+
+public:
+
+ explicit BackingComp();
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire ( ) noexcept override;
+ virtual void SAL_CALL release ( ) noexcept override;
+
+ // XTypeProvide
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes () override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName ( ) override;
+ virtual sal_Bool SAL_CALL supportsService ( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& lArgs ) override;
+
+ // XController
+ virtual void SAL_CALL attachFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) override;
+ virtual sal_Bool SAL_CALL attachModel( const css::uno::Reference< css::frame::XModel >& xModel ) override;
+ virtual sal_Bool SAL_CALL suspend( sal_Bool bSuspend ) override;
+ virtual css::uno::Any SAL_CALL getViewData() override;
+ virtual void SAL_CALL restoreViewData( const css::uno::Any& aData ) override;
+ virtual css::uno::Reference< css::frame::XModel > SAL_CALL getModel() override;
+ virtual css::uno::Reference< css::frame::XFrame > SAL_CALL getFrame() override;
+
+ // XKeyListener
+ virtual void SAL_CALL keyPressed ( const css::awt::KeyEvent& aEvent ) override;
+ virtual void SAL_CALL keyReleased( const css::awt::KeyEvent& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose ( ) override;
+ virtual void SAL_CALL addEventListener ( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& aURL, const OUString& sTargetFrameName , sal_Int32 nSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xListener, const css::util::URL& aURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xListener, const css::util::URL& aURL ) override;
+};
+
+BackingComp::BackingComp()
+{
+}
+
+/** return information about supported interfaces.
+
+ Some interfaces are supported by his class directly, but some other ones are
+ used by aggregation. An instance of this class must provide some window interfaces.
+ But it must represent a VCL window behind such interfaces too! So we use an internal
+ saved window member to ask it for its interfaces and return it. But we must be aware then,
+ that it can be destroyed from outside too ...
+
+ @param aType
+ describe the required interface type
+
+ @return An Any holding the instance, which provides the queried interface.
+ Note: There exist two possible results ... this instance itself and her window member!
+ */
+
+css::uno::Any SAL_CALL BackingComp::queryInterface( /*IN*/ const css::uno::Type& aType )
+{
+ // first look for own supported interfaces
+ css::uno::Any aResult = ::cppu::queryInterface(
+ aType,
+ static_cast< css::lang::XTypeProvider* >(this),
+ static_cast< css::lang::XServiceInfo* >(this),
+ static_cast< css::lang::XInitialization* >(this),
+ static_cast< css::frame::XController* >(this),
+ static_cast< css::lang::XComponent* >(this),
+ static_cast< css::lang::XEventListener* >(this),
+ static_cast< css::awt::XKeyListener* >(static_cast< css::lang::XEventListener* >(this)),
+ static_cast< css::frame::XDispatchProvider* >(this),
+ static_cast< css::frame::XDispatch* >(this) );
+
+ // then look for supported window interfaces
+ // Note: They exist only, if this instance was initialized
+ // with a valid window reference. It's aggregation on demand ...
+ if (!aResult.hasValue())
+ {
+ /* SAFE { */
+ SolarMutexGuard aGuard;
+ if (m_xWindow.is())
+ aResult = m_xWindow->queryInterface(aType);
+ /* } SAFE */
+ }
+
+ // look for XWeak and XInterface
+ if (!aResult.hasValue())
+ aResult = OWeakObject::queryInterface(aType);
+
+ return aResult;
+}
+
+
+/** increase ref count of this instance.
+ */
+
+void SAL_CALL BackingComp::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+/** decrease ref count of this instance.
+ */
+
+void SAL_CALL BackingComp::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+
+/** return collection about all supported interfaces.
+
+ Optimize this method !
+ We initialize a static variable only one time.
+ And we don't must use a mutex at every call!
+ For the first call; pTypeCollection is NULL -
+ for the second call pTypeCollection is different from NULL!
+
+ @return A list of all supported interface types.
+*/
+
+css::uno::Sequence< css::uno::Type > SAL_CALL BackingComp::getTypes()
+{
+ static cppu::OTypeCollection aTypeCollection = [this]() {
+ SolarMutexGuard aGuard;
+ css::uno::Reference<css::lang::XTypeProvider> xProvider(m_xWindow, css::uno::UNO_QUERY);
+
+ css::uno::Sequence<css::uno::Type> lWindowTypes;
+ if (xProvider.is())
+ lWindowTypes = xProvider->getTypes();
+
+ return cppu::OTypeCollection(
+ cppu::UnoType<css::lang::XInitialization>::get(),
+ cppu::UnoType<css::lang::XTypeProvider>::get(),
+ cppu::UnoType<css::lang::XServiceInfo>::get(),
+ cppu::UnoType<css::frame::XController>::get(),
+ cppu::UnoType<css::lang::XComponent>::get(),
+ cppu::UnoType<css::frame::XDispatchProvider>::get(),
+ cppu::UnoType<css::frame::XDispatch>::get(), lWindowTypes);
+ }();
+
+ return aTypeCollection.getTypes();
+}
+
+
+/** create one unique Id for all instances of this class.
+
+ Optimize this method
+ We initialize a static variable only one time. And we don't must use a mutex at every call!
+ For the first call; pID is NULL - for the second call pID is different from NULL!
+
+ @return A byte array, which represent the unique id.
+*/
+
+css::uno::Sequence< sal_Int8 > SAL_CALL BackingComp::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString SAL_CALL BackingComp::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.BackingComp";
+}
+
+sal_Bool SAL_CALL BackingComp::supportsService( /*IN*/ const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL BackingComp::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.StartModule", "com.sun.star.frame.ProtocolHandler" };
+}
+
+
+/**
+ attach this component to a target frame.
+
+ We have to use the container window of this frame as parent window of our own component window.
+ But it's not allowed to work with it really. May another component used it too.
+ Currently we need it only to create our child component window and support it's
+ interfaces inside our queryInterface() method. The user of us must have e.g. the
+ XWindow interface of it to be able to call setComponent(xWindow,xController) at the
+ frame!
+
+ May he will do the following things:
+
+ <listing>
+ XController xBackingComp = (XController)UnoRuntime.queryInterface(
+ XController.class,
+ xSMGR.createInstance(SERVICENAME_STARTMODULE));
+
+ // at this time XWindow isn't present at this instance!
+ XWindow xBackingComp = (XWindow)UnoRuntime.queryInterface(
+ XWindow.class,
+ xBackingComp);
+
+ // attach controller to the frame
+ // We will use its container window, to create
+ // the component window. From now we offer the window interfaces!
+ xBackingComp.attachFrame(xFrame);
+
+ XWindow xBackingComp = (XWindow)UnoRuntime.queryInterface(
+ XWindow.class,
+ xBackingComp);
+
+ // Our user can set us at the frame as new component
+ xFrame.setComponent(xBackingWin, xBackingComp);
+
+ // But that had no effect to our view state.
+ // We must be started to create our UI elements like e.g. menu, title, background ...
+ XInitialization xBackingInit = (XInitialization)UnoRuntime.queryInterface(
+ XInitialization.class,
+ xBackingComp);
+
+ xBackingInit.initialize(lArgs);
+ </listing>
+
+ @param xFrame
+ reference to our new target frame
+
+ @throw css::uno::RuntimeException
+ if the given frame reference is wrong or component window couldn't be created
+ successfully.
+ We throw it too, if we already attached to a frame. Because we don't support
+ reparenting of our component window on demand!
+*/
+
+void SAL_CALL BackingComp::attachFrame( /*IN*/ const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ /* SAFE */
+ SolarMutexGuard aGuard;
+
+ // check some required states
+ if (m_xFrame.is())
+ throw css::uno::RuntimeException(
+ "already attached",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ if (!xFrame.is())
+ throw css::uno::RuntimeException(
+ "invalid frame reference",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ if (!m_xWindow.is())
+ return; // disposed
+
+ // safe the frame reference
+ m_xFrame = xFrame;
+
+ // initialize the component and its parent window
+ css::uno::Reference< css::awt::XWindow > xParentWindow = xFrame->getContainerWindow();
+ VclPtr< WorkWindow > pParent = static_cast<WorkWindow*>(VCLUnoHelper::GetWindow(xParentWindow));
+ VclPtr< vcl::Window > pWindow = VCLUnoHelper::GetWindow(m_xWindow);
+
+ // disable full screen mode of the frame!
+ if (pParent && pParent->IsFullScreenMode())
+ {
+ pParent->ShowFullScreenMode(false);
+ pParent->SetMenuBarMode(MenuBarMode::Normal);
+ }
+
+ // create the menu bar for the backing component
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(m_xFrame, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ if (xLayoutManager.is())
+ {
+ xLayoutManager->lock();
+ xLayoutManager->createElement("private:resource/menubar/menubar");
+ xLayoutManager->unlock();
+ }
+
+ if (pWindow)
+ {
+ // set help ID for our canvas
+ pWindow->SetHelpId("FWK_HID_BACKINGWINDOW");
+ }
+
+ // inform BackingWindow about frame
+ BackingWindow* pBack = dynamic_cast<BackingWindow*>(pWindow.get());
+ if( pBack )
+ pBack->setOwningFrame( m_xFrame );
+
+ // Set a minimum size for Start Center
+ if( !pParent || !pBack )
+ return;
+
+ tools::Long nMenuHeight = 0;
+ vcl::Window* pMenu = pParent->GetWindow(GetWindowType::Next);
+ if( pMenu )
+ nMenuHeight = pMenu->GetSizePixel().Height();
+
+ m_aInitialWindowMinSize = pParent->GetMinOutputSizePixel();
+ if (!m_aInitialWindowMinSize.Width())
+ m_aInitialWindowMinSize.AdjustWidth(1);
+ if (!m_aInitialWindowMinSize.Height())
+ m_aInitialWindowMinSize.AdjustHeight(1);
+
+ pParent->SetMinOutputSizePixel(
+ Size(
+ pBack->get_width_request(),
+ pBack->get_height_request() + nMenuHeight));
+
+ /* } SAFE */
+}
+
+
+/** not supported.
+
+ This component does not know any model. It will be represented by a window and
+ its controller only.
+
+ return <FALSE/> every time.
+ */
+
+sal_Bool SAL_CALL BackingComp::attachModel( /*IN*/ const css::uno::Reference< css::frame::XModel >& )
+{
+ return false;
+}
+
+
+/** not supported.
+
+ This component does not know any model. It will be represented by a window and
+ its controller only.
+
+ return An empty reference every time.
+ */
+
+css::uno::Reference< css::frame::XModel > SAL_CALL BackingComp::getModel()
+{
+ return css::uno::Reference< css::frame::XModel >();
+}
+
+
+/** not supported.
+
+ return An empty value.
+ */
+
+css::uno::Any SAL_CALL BackingComp::getViewData()
+{
+ return css::uno::Any();
+}
+
+
+/** not supported.
+
+ @param aData
+ not used.
+ */
+
+void SAL_CALL BackingComp::restoreViewData( /*IN*/ const css::uno::Any& )
+{
+}
+
+
+/** returns the attached frame for this component.
+
+ @see attachFrame()
+
+ @return The internally saved frame reference.
+ Can be null, if attachFrame() was not called before.
+ */
+
+css::uno::Reference< css::frame::XFrame > SAL_CALL BackingComp::getFrame()
+{
+ /* SAFE { */
+ SolarMutexGuard aGuard;
+ return m_xFrame;
+ /* } SAFE */
+}
+
+
+/** ask controller for its current working state.
+
+ If someone wishes to close this component, it must suspend the controller before.
+ That will be a chance for it to disagree with that AND show any UI for a possible
+ UI user.
+
+ @param bSuspend
+ If it's set to sal_True this controller should be suspended.
+ sal_False will resuspend it.
+
+ @return sal_True if the request could be finished successfully; sal_False otherwise.
+ */
+
+sal_Bool SAL_CALL BackingComp::suspend( /*IN*/ sal_Bool )
+{
+ /* FIXME ... implemented by using default :-( */
+ return true;
+}
+
+
+/** callback from our window member.
+
+ Our internal saved window wish to die. It will be disposed from outside (may be the frame)
+ and inform us. We must release its reference only here. Of course we check the given reference
+ here and reject callback from unknown sources.
+
+ Note: deregistration as listener isn't necessary here. The broadcaster do it automatically.
+
+ @param aEvent
+ describe the broadcaster of this callback
+
+ @throw css::uno::RuntimeException
+ if the broadcaster doesn't represent the expected window reference.
+*/
+
+void SAL_CALL BackingComp::disposing( /*IN*/ const css::lang::EventObject& aEvent )
+{
+ // Attention: don't free m_pAccExec here! see comments inside dtor and
+ // keyPressed() for further details.
+
+ /* SAFE { */
+ SolarMutexGuard aGuard;
+
+ if (!aEvent.Source.is() || aEvent.Source!=m_xWindow || !m_xWindow.is())
+ throw css::uno::RuntimeException(
+ "unexpected source or called twice",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ m_xWindow.clear();
+
+ /* } SAFE */
+}
+
+
+/** kill this instance.
+
+ It can be called from our owner frame only. But there is no possibility to check the caller.
+ We have to release all our internal used resources and die. From this point we can throw
+ DisposedExceptions for every further interface request... but current implementation doesn't do so...
+
+*/
+
+void SAL_CALL BackingComp::dispose()
+{
+ /* SAFE { */
+ SolarMutexGuard aGuard;
+
+ if (m_xFrame.is())
+ {
+ css::uno::Reference< css::awt::XWindow > xParentWindow = m_xFrame->getContainerWindow();
+ VclPtr< WorkWindow > pParent = static_cast<WorkWindow*>(VCLUnoHelper::GetWindow(xParentWindow));
+ if (pParent)
+ {
+ pParent->SetMinOutputSizePixel(m_aInitialWindowMinSize);
+ // hide NotebookBar
+ sfx2::SfxNotebookBar::CloseMethod(static_cast<SystemWindow*>(pParent));
+ }
+ }
+
+ // stop listening at the window
+ if (m_xWindow.is())
+ {
+ m_xWindow->removeEventListener(this);
+ m_xWindow->removeKeyListener(this);
+ m_xWindow.clear();
+ }
+
+ // forget all other used references
+ m_xFrame.clear();
+
+ /* } SAFE */
+}
+
+
+/** not supported.
+
+ @param xListener
+ not used.
+
+ @throw css::uno::RuntimeException
+ because the listener expect to be holded alive by this container.
+ We must inform it about this unsupported feature.
+ */
+
+void SAL_CALL BackingComp::addEventListener( /*IN*/ const css::uno::Reference< css::lang::XEventListener >& )
+{
+ throw css::uno::RuntimeException(
+ "not supported",
+ static_cast< ::cppu::OWeakObject* >(this));
+}
+
+
+/** not supported.
+
+ Because registration is not supported too, we must do nothing here. Nobody can call this method really.
+
+ @param xListener
+ not used.
+ */
+
+void SAL_CALL BackingComp::removeEventListener( /*IN*/ const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+
+/**
+ force initialization for this component.
+
+ Inside attachFrame() we created our component window. But it was not allowed there, to
+ initialize it. E.g. the menu must be set at the container window of the frame, which
+ is our parent window. But may at that time another component used it.
+ That's why our creator has to inform us, when it's time to initialize us really.
+ Currently only calling of this method must be done. But further implementations
+ can use special in parameter to configure this initialization...
+
+ @param lArgs
+ currently not used
+
+ @throw css::uno::RuntimeException
+ if some resources are missing
+ Means if may be attachedFrame() wasn't called before.
+ */
+
+void SAL_CALL BackingComp::initialize( /*IN*/ const css::uno::Sequence< css::uno::Any >& lArgs )
+{
+ /* SAFE { */
+ SolarMutexGuard aGuard;
+
+ if (m_xWindow.is())
+ throw css::uno::Exception(
+ "already initialized",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ css::uno::Reference< css::awt::XWindow > xParentWindow;
+ if (
+ (lArgs.getLength()!=1 ) ||
+ (!(lArgs[0] >>= xParentWindow)) ||
+ (!xParentWindow.is() )
+ )
+ {
+ throw css::uno::Exception(
+ "wrong or corrupt argument list",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ // create the component window
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(xParentWindow);
+ VclPtr<vcl::Window> pWindow = VclPtr<BackingWindow>::Create(pParent);
+ m_xWindow = VCLUnoHelper::GetInterface(pWindow);
+
+ if (!m_xWindow.is())
+ throw css::uno::RuntimeException(
+ "couldn't create component window",
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ // start listening for window disposing
+ // It's set at our owner frame as component window later too. So it will may be disposed there ...
+ m_xWindow->addEventListener(static_cast< css::lang::XEventListener* >(this));
+
+ m_xWindow->setVisible(true);
+
+ /* } SAFE */
+}
+
+
+void SAL_CALL BackingComp::keyPressed( /*IN*/ const css::awt::KeyEvent& )
+{
+}
+
+
+void SAL_CALL BackingComp::keyReleased( /*IN*/ const css::awt::KeyEvent& )
+{
+ /* Attention
+ Please use keyPressed() instead of this method. Otherwise it would be possible, that
+ - a key input may be first switch to the backing mode
+ - and this component register itself as key listener too
+ - and it's first event will be a keyReleased() for the already well known event, which switched to the backing mode!
+ So it will be handled twice! document => backing mode => exit app...
+ */
+}
+
+// XDispatchProvider
+css::uno::Reference< css::frame::XDispatch > SAL_CALL BackingComp::queryDispatch( const css::util::URL& aURL, const OUString& /*sTargetFrameName*/, sal_Int32 /*nSearchFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+ if ( aURL.Protocol == "vnd.org.libreoffice.recentdocs:" )
+ xDispatch = this;
+
+ return xDispatch;
+}
+
+css::uno::Sequence < css::uno::Reference< css::frame::XDispatch > > SAL_CALL BackingComp::queryDispatches( const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescripts )
+{
+ sal_Int32 nCount = seqDescripts.getLength();
+ css::uno::Sequence < css::uno::Reference < XDispatch > > lDispatcher( nCount );
+
+ std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.getArray(),
+ [this](const css::frame::DispatchDescriptor& rDesc) -> css::uno::Reference<XDispatch> {
+ return queryDispatch(rDesc.FeatureURL, rDesc.FrameName, rDesc.SearchFlags); });
+
+ return lDispatcher;
+}
+
+// XDispatch
+void SAL_CALL BackingComp::dispatch( const css::util::URL& aURL, const css::uno::Sequence < css::beans::PropertyValue >& /*lArgs*/ )
+{
+ // vnd.org.libreoffice.recentdocs:ClearRecentFileList - clear recent files
+ if ( aURL.Path != "ClearRecentFileList" )
+ return;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(m_xWindow);
+ BackingWindow* pBack = dynamic_cast<BackingWindow*>(pWindow.get());
+ if( !pBack )
+ return;
+
+ pBack->clearRecentFileList();
+
+ // Recalculate minimum width
+ css::uno::Reference< css::awt::XWindow > xParentWindow = m_xFrame->getContainerWindow();
+ VclPtr< WorkWindow > pParent = static_cast<WorkWindow*>(VCLUnoHelper::GetWindow(xParentWindow));
+ if( pParent )
+ {
+ pParent->SetMinOutputSizePixel( Size(
+ pBack->get_width_request(),
+ pParent->GetMinOutputSizePixel().Height()) );
+ }
+}
+
+void SAL_CALL BackingComp::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xControl*/, const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL BackingComp::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xControl*/, const css::util::URL& /*aURL*/ )
+{
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_sfx2_BackingComp_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new BackingComp);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/backingwindow.cxx b/sfx2/source/dialog/backingwindow.cxx
new file mode 100644
index 000000000..6d8c2bbbc
--- /dev/null
+++ b/sfx2/source/dialog/backingwindow.cxx
@@ -0,0 +1,780 @@
+/* -*- 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 "backingwindow.hxx"
+#include <vcl/event.hxx>
+#include <vcl/help.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+
+#include <unotools/historyoptions.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svtools/openfiledroptargetlistener.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svtools/langhelp.hxx>
+#include <templateviewitem.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <sfx2/app.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::document;
+
+constexpr OUStringLiteral SERVICENAME_CFGREADACCESS = u"com.sun.star.configuration.ConfigurationAccess";
+
+class BrandImage final : public weld::CustomWidgetController
+{
+private:
+ BitmapEx maBrandImage;
+ bool mbIsDark = false;
+ Size m_BmpSize;
+
+public:
+ Size getSize() { return m_BmpSize; }
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
+ {
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ rDevice.SetBackground(Wallpaper(rStyleSettings.GetWindowColor()));
+
+ SetPointer(PointerStyle::RefHand);
+ }
+
+ virtual void Resize() override
+ {
+ auto nWidth = GetOutputSizePixel().Width();
+ if (maBrandImage.GetSizePixel().Width() != nWidth)
+ LoadImageForWidth(nWidth);
+ weld::CustomWidgetController::Resize();
+ }
+
+ void LoadImageForWidth(int nWidth)
+ {
+ mbIsDark = Application::GetSettings().GetStyleSettings().GetDialogColor().IsDark();
+ SfxApplication::loadBrandSvg(mbIsDark ? "shell/logo-sc_inverted" : "shell/logo-sc",
+ maBrandImage, nWidth);
+ }
+
+ void ConfigureForWidth(int nWidth)
+ {
+ if (maBrandImage.GetSizePixel().Width() == nWidth)
+ return;
+ LoadImageForWidth(nWidth);
+ m_BmpSize = maBrandImage.GetSizePixel();
+ set_size_request(m_BmpSize.Width(), m_BmpSize.Height());
+ }
+
+ virtual void StyleUpdated() override
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ // tdf#141857 update background to current theme
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ rDevice.SetBackground(Wallpaper(rStyleSettings.GetWindowColor()));
+
+ const bool bIsDark = rStyleSettings.GetDialogColor().IsDark();
+ if (bIsDark != mbIsDark)
+ LoadImageForWidth(GetOutputSizePixel().Width());
+ weld::CustomWidgetController::StyleUpdated();
+ }
+
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override
+ {
+ if (rMEvt.IsLeft())
+ {
+ OUString sURL = officecfg::Office::Common::Menus::VolunteerURL::get();
+ localizeWebserviceURI(sURL);
+
+ Reference<css::system::XSystemShellExecute> const xSystemShellExecute(
+ css::system::SystemShellExecute::create(
+ ::comphelper::getProcessComponentContext()));
+ xSystemShellExecute->execute(sURL, OUString(),
+ css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+ return true;
+ }
+
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override
+ {
+ rRenderContext.DrawBitmapEx(Point(0, 0), maBrandImage);
+ }
+};
+
+// increase size of the text in the buttons on the left fMultiplier-times
+float const g_fMultiplier = 1.2f;
+
+BackingWindow::BackingWindow(vcl::Window* i_pParent)
+ : InterimItemWindow(i_pParent, "sfx/ui/startcenter.ui", "StartCenter", false)
+ , mxOpenButton(m_xBuilder->weld_button("open_all"))
+ , mxRecentButton(m_xBuilder->weld_menu_toggle_button("open_recent"))
+ , mxRemoteButton(m_xBuilder->weld_button("open_remote"))
+ , mxTemplateButton(m_xBuilder->weld_menu_toggle_button("templates_all"))
+ , mxCreateLabel(m_xBuilder->weld_label("create_label"))
+ , mxAltHelpLabel(m_xBuilder->weld_label("althelplabel"))
+ , mxWriterAllButton(m_xBuilder->weld_button("writer_all"))
+ , mxCalcAllButton(m_xBuilder->weld_button("calc_all"))
+ , mxImpressAllButton(m_xBuilder->weld_button("impress_all"))
+ , mxDrawAllButton(m_xBuilder->weld_button("draw_all"))
+ , mxDBAllButton(m_xBuilder->weld_button("database_all"))
+ , mxMathAllButton(m_xBuilder->weld_button("math_all"))
+ , mxBrandImage(new BrandImage)
+ , mxBrandImageWeld(new weld::CustomWeld(*m_xBuilder, "daBrand", *mxBrandImage))
+ , mxHelpButton(m_xBuilder->weld_button("help"))
+ , mxExtensionsButton(m_xBuilder->weld_button("extensions"))
+ , mxAllButtonsBox(m_xBuilder->weld_container("all_buttons_box"))
+ , mxButtonsBox(m_xBuilder->weld_container("buttons_box"))
+ , mxSmallButtonsBox(m_xBuilder->weld_container("small_buttons_box"))
+ , mxAllRecentThumbnails(new sfx2::RecentDocsView(m_xBuilder->weld_scrolled_window("scrollrecent", true),
+ m_xBuilder->weld_menu("recentmenu")))
+ , mxAllRecentThumbnailsWin(new weld::CustomWeld(*m_xBuilder, "all_recent", *mxAllRecentThumbnails))
+ , mxLocalView(new TemplateDefaultView(m_xBuilder->weld_scrolled_window("scrolllocal", true),
+ m_xBuilder->weld_menu("localmenu")))
+ , mxLocalViewWin(new weld::CustomWeld(*m_xBuilder, "local_view", *mxLocalView))
+ , mbLocalViewInitialized(false)
+ , mbInitControls(false)
+{
+ // init background
+ SetPaintTransparent(false);
+ SetBackground(svtools::ColorConfig().GetColorValue(::svtools::APPBACKGROUND).nColor);
+
+ //set an alternative help label that doesn't hotkey the H of the Help menu
+ mxHelpButton->set_label(mxAltHelpLabel->get_label());
+ mxHelpButton->connect_clicked(LINK(this, BackingWindow, ClickHelpHdl));
+
+ mxDropTarget = mxAllRecentThumbnails->GetDropTarget();
+
+ try
+ {
+ mxContext.set( ::comphelper::getProcessComponentContext(), uno::UNO_SET_THROW );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "fwk", "BackingWindow" );
+ }
+
+ SetStyle( GetStyle() | WB_DIALOGCONTROL );
+
+ // get dispatch provider
+ Reference<XDesktop2> xDesktop = Desktop::create( comphelper::getProcessComponentContext() );
+ mxDesktopDispatchProvider = xDesktop;
+
+}
+
+IMPL_LINK(BackingWindow, ClickHelpHdl, weld::Button&, rButton, void)
+{
+ if (Help* pHelp = Application::GetHelp())
+ pHelp->Start(OUString::fromUtf8(m_xContainer->get_help_id()), &rButton);
+}
+
+BackingWindow::~BackingWindow()
+{
+ disposeOnce();
+}
+
+void BackingWindow::dispose()
+{
+ // deregister drag&drop helper
+ if (mxDropTargetListener.is())
+ {
+ if (mxDropTarget.is())
+ {
+ mxDropTarget->removeDropTargetListener(mxDropTargetListener);
+ mxDropTarget->setActive(false);
+ }
+ mxDropTargetListener.clear();
+ }
+ mxDropTarget.clear();
+ mxOpenButton.reset();
+ mxRemoteButton.reset();
+ mxRecentButton.reset();
+ mxTemplateButton.reset();
+ mxCreateLabel.reset();
+ mxAltHelpLabel.reset();
+ mxWriterAllButton.reset();
+ mxCalcAllButton.reset();
+ mxImpressAllButton.reset();
+ mxDrawAllButton.reset();
+ mxDBAllButton.reset();
+ mxMathAllButton.reset();
+ mxBrandImageWeld.reset();
+ mxBrandImage.reset();
+ mxHelpButton.reset();
+ mxExtensionsButton.reset();
+ mxAllButtonsBox.reset();
+ mxButtonsBox.reset();
+ mxSmallButtonsBox.reset();
+ mxAllRecentThumbnailsWin.reset();
+ mxAllRecentThumbnails.reset();
+ mxLocalViewWin.reset();
+ mxLocalView.reset();
+ InterimItemWindow::dispose();
+}
+
+void BackingWindow::initControls()
+{
+ if( mbInitControls )
+ return;
+
+ mbInitControls = true;
+
+ // collect the URLs of the entries in the File/New menu
+ SvtModuleOptions aModuleOptions;
+
+ if (aModuleOptions.IsModuleInstalled(SvtModuleOptions::EModule::WRITER))
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_WRITER;
+
+ if (aModuleOptions.IsModuleInstalled(SvtModuleOptions::EModule::CALC))
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_CALC;
+
+ if (aModuleOptions.IsModuleInstalled(SvtModuleOptions::EModule::IMPRESS))
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_IMPRESS;
+
+ if (aModuleOptions.IsModuleInstalled(SvtModuleOptions::EModule::DRAW))
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_DRAW;
+
+ if (aModuleOptions.IsModuleInstalled(SvtModuleOptions::EModule::DATABASE))
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_DATABASE;
+
+ if (aModuleOptions.IsModuleInstalled(SvtModuleOptions::EModule::MATH))
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_MATH;
+
+ mxAllRecentThumbnails->mnFileTypes |= sfx2::ApplicationType::TYPE_OTHER;
+ mxAllRecentThumbnails->Reload();
+ mxAllRecentThumbnails->ShowTooltips( true );
+ mxRecentButton->set_active(true);
+ mxRecentButton->grab_focus();
+
+ //initialize Template view
+ mxLocalView->Hide();
+
+ //set handlers
+ mxLocalView->setCreateContextMenuHdl(LINK(this, BackingWindow, CreateContextMenuHdl));
+ mxLocalView->setOpenTemplateHdl(LINK(this, BackingWindow, OpenTemplateHdl));
+ mxLocalView->setEditTemplateHdl(LINK(this, BackingWindow, EditTemplateHdl));
+ mxLocalView->ShowTooltips( true );
+
+ checkInstalledModules();
+
+ mxExtensionsButton->connect_clicked(LINK(this, BackingWindow, ExtLinkClickHdl));
+
+ mxOpenButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxRemoteButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxRecentButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxTemplateButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxWriterAllButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxDrawAllButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxCalcAllButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxDBAllButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxImpressAllButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+ mxMathAllButton->connect_clicked(LINK(this, BackingWindow, ClickHdl));
+
+ mxRecentButton->connect_selected(LINK(this, BackingWindow, MenuSelectHdl));
+ mxTemplateButton->connect_selected(LINK(this, BackingWindow, MenuSelectHdl));
+
+ ApplyStyleSettings();
+}
+
+void BackingWindow::DataChanged(const DataChangedEvent& rDCEvt)
+{
+ if ((rDCEvt.GetType() != DataChangedEventType::SETTINGS)
+ || !(rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
+ {
+ InterimItemWindow::DataChanged(rDCEvt);
+ return;
+ }
+
+ ApplyStyleSettings();
+ Invalidate();
+}
+
+template <typename WidgetClass>
+void BackingWindow::setLargerFont(WidgetClass& pWidget, const vcl::Font& rFont)
+{
+ vcl::Font aFont(rFont);
+ aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * g_fMultiplier));
+ pWidget->set_font(aFont);
+}
+
+void BackingWindow::ApplyStyleSettings()
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ const Color aButtonsBackground(rStyleSettings.GetWindowColor());
+ const vcl::Font& aButtonFont(rStyleSettings.GetPushButtonFont());
+ const vcl::Font& aLabelFont(rStyleSettings.GetLabelFont());
+
+ // setup larger fonts
+ setLargerFont(mxOpenButton, aButtonFont);
+ setLargerFont(mxOpenButton, aButtonFont);
+ setLargerFont(mxRemoteButton, aButtonFont);
+ setLargerFont(mxRecentButton, aButtonFont);
+ setLargerFont(mxTemplateButton, aButtonFont);
+ setLargerFont(mxWriterAllButton, aButtonFont);
+ setLargerFont(mxDrawAllButton, aButtonFont);
+ setLargerFont(mxCalcAllButton, aButtonFont);
+ setLargerFont(mxDBAllButton, aButtonFont);
+ setLargerFont(mxImpressAllButton, aButtonFont);
+ setLargerFont(mxMathAllButton, aButtonFont);
+ setLargerFont(mxCreateLabel, aLabelFont);
+
+ mxAllButtonsBox->set_background(aButtonsBackground);
+ mxSmallButtonsBox->set_background(aButtonsBackground);
+
+ // compute the menubar height
+ sal_Int32 nMenuHeight = 0;
+ if (SystemWindow* pSystemWindow = GetSystemWindow())
+ nMenuHeight = pSystemWindow->GetMenuBarHeight();
+
+ // fdo#34392: we do the layout dynamically, the layout depends on the font,
+ // so we should handle data changed events (font changing) of the last child
+ // control, at this point all the controls have updated settings (i.e. font).
+ Size aPrefSize(mxAllButtonsBox->get_preferred_size());
+ set_width_request(aPrefSize.Width());
+
+ // Now set a brand image wide enough to fill this width
+ weld::DrawingArea* pDrawingArea = mxBrandImage->GetDrawingArea();
+ mxBrandImage->ConfigureForWidth(aPrefSize.Width() -
+ (pDrawingArea->get_margin_start() + pDrawingArea->get_margin_end()));
+ // Refetch because the brand image height to match this width is now set
+ aPrefSize = mxAllButtonsBox->get_preferred_size();
+
+ set_height_request(nMenuHeight + aPrefSize.Height() + mxBrandImage->getSize().getHeight());
+}
+
+void BackingWindow::initializeLocalView()
+{
+ if (!mbLocalViewInitialized)
+ {
+ mbLocalViewInitialized = true;
+ mxLocalView->Populate();
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::NONE));
+ mxLocalView->showAllTemplates();
+ }
+}
+
+void BackingWindow::checkInstalledModules()
+{
+ SvtModuleOptions aModuleOpt;
+
+ mxWriterAllButton->set_sensitive( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ));
+ mxCalcAllButton->set_sensitive( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) );
+ mxImpressAllButton->set_sensitive( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) );
+ mxDrawAllButton->set_sensitive( aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) );
+ mxMathAllButton->set_sensitive(aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ));
+ mxDBAllButton->set_sensitive(aModuleOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ));
+}
+
+bool BackingWindow::PreNotify(NotifyEvent& rNEvt)
+{
+ if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ const KeyEvent* pEvt = rNEvt.GetKeyEvent();
+ const vcl::KeyCode& rKeyCode(pEvt->GetKeyCode());
+
+ bool bThumbnailHasFocus = mxAllRecentThumbnails->HasFocus() || mxLocalView->HasFocus();
+
+ // Subwindows of BackingWindow: Sidebar and Thumbnail view
+ if( rKeyCode.GetCode() == KEY_F6 )
+ {
+ if( rKeyCode.IsShift() ) // Shift + F6
+ {
+ if (bThumbnailHasFocus)
+ {
+ mxOpenButton->grab_focus();
+ return true;
+ }
+ }
+ else if ( rKeyCode.IsMod1() ) // Ctrl + F6
+ {
+ if(mxAllRecentThumbnails->IsVisible())
+ {
+ mxAllRecentThumbnails->GrabFocus();
+ return true;
+ }
+ else if(mxLocalView->IsVisible())
+ {
+ mxLocalView->GrabFocus();
+ return true;
+ }
+ }
+ else // F6
+ {
+ if (!bThumbnailHasFocus)
+ {
+ if(mxAllRecentThumbnails->IsVisible())
+ {
+ mxAllRecentThumbnails->GrabFocus();
+ return true;
+ }
+ else if(mxLocalView->IsVisible())
+ {
+ mxLocalView->GrabFocus();
+ return true;
+ }
+ }
+ }
+ }
+
+ // try the 'normal' accelerators (so that eg. Ctrl+Q works)
+ if (!mpAccExec)
+ {
+ mpAccExec = svt::AcceleratorExecute::createAcceleratorHelper();
+ mpAccExec->init( comphelper::getProcessComponentContext(), mxFrame);
+ }
+
+ const OUString aCommand = mpAccExec->findCommand(svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyCode));
+ if ((aCommand != "vnd.sun.star.findbar:FocusToFindbar") && pEvt && mpAccExec->execute(rKeyCode))
+ return true;
+ }
+ return InterimItemWindow::PreNotify( rNEvt );
+}
+
+void BackingWindow::GetFocus()
+{
+ GetFocusFlags nFlags = GetParent()->GetGetFocusFlags();
+ if( nFlags & GetFocusFlags::F6 )
+ {
+ if( nFlags & GetFocusFlags::Forward ) // F6
+ {
+ mxOpenButton->grab_focus();
+ return;
+ }
+ else // Shift + F6 or Ctrl + F6
+ {
+ if(mxAllRecentThumbnails->IsVisible())
+ mxAllRecentThumbnails->GrabFocus();
+ else if(mxLocalView->IsVisible())
+ mxLocalView->GrabFocus();
+ return;
+ }
+ }
+ InterimItemWindow::GetFocus();
+}
+
+void BackingWindow::setOwningFrame( const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ mxFrame = xFrame;
+ if( ! mbInitControls )
+ initControls();
+
+ // establish drag&drop mode
+ mxDropTargetListener.set(new OpenFileDropTargetListener(mxContext, mxFrame));
+
+ if (mxDropTarget.is())
+ {
+ mxDropTarget->addDropTargetListener(mxDropTargetListener);
+ mxDropTarget->setActive(true);
+ }
+
+ css::uno::Reference<XFramesSupplier> xFramesSupplier(mxDesktopDispatchProvider, UNO_QUERY);
+ if (xFramesSupplier)
+ xFramesSupplier->setActiveFrame(mxFrame);
+}
+
+IMPL_LINK(BackingWindow, ExtLinkClickHdl, weld::Button&, rButton, void)
+{
+ OUString aNode;
+
+ if (&rButton == mxExtensionsButton.get())
+ aNode = "AddFeatureURL";
+
+ if (aNode.isEmpty())
+ return;
+
+ try
+ {
+ uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.Common/Help/StartCenter"))}
+ }));
+
+ Reference<lang::XMultiServiceFactory> xConfig = configuration::theDefaultProvider::get( comphelper::getProcessComponentContext() );
+ Reference<container::XNameAccess> xNameAccess(xConfig->createInstanceWithArguments(SERVICENAME_CFGREADACCESS, args), UNO_QUERY);
+ if (xNameAccess.is())
+ {
+ OUString sURL;
+ Any value(xNameAccess->getByName(aNode));
+
+ sURL = value.get<OUString>();
+ localizeWebserviceURI(sURL);
+
+ Reference<css::system::XSystemShellExecute> const
+ xSystemShellExecute(
+ css::system::SystemShellExecute::create(
+ ::comphelper::getProcessComponentContext()));
+ xSystemShellExecute->execute(sURL, OUString(),
+ css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+IMPL_LINK( BackingWindow, ClickHdl, weld::Button&, rButton, void )
+{
+ // dispatch the appropriate URL and end the dialog
+ if( &rButton == mxWriterAllButton.get() )
+ dispatchURL( "private:factory/swriter" );
+ else if( &rButton == mxCalcAllButton.get() )
+ dispatchURL( "private:factory/scalc" );
+ else if( &rButton == mxImpressAllButton.get() )
+ dispatchURL( "private:factory/simpress?slot=6686" );
+ else if( &rButton == mxDrawAllButton.get() )
+ dispatchURL( "private:factory/sdraw" );
+ else if( &rButton == mxDBAllButton.get() )
+ dispatchURL( "private:factory/sdatabase?Interactive" );
+ else if( &rButton == mxMathAllButton.get() )
+ dispatchURL( "private:factory/smath" );
+ else if( &rButton == mxOpenButton.get() )
+ {
+ Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY );
+
+ dispatchURL( ".uno:Open", OUString(), xFrame, { comphelper::makePropertyValue("Referer", OUString("private:user")) } );
+ }
+ else if( &rButton == mxRemoteButton.get() )
+ {
+ Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY );
+
+ dispatchURL( ".uno:OpenRemote", OUString(), xFrame, {} );
+ }
+ else if( &rButton == mxRecentButton.get() )
+ {
+ mxLocalView->Hide();
+ mxAllRecentThumbnails->Show();
+ mxAllRecentThumbnails->GrabFocus();
+ mxRecentButton->set_active(true);
+ mxTemplateButton->set_active(false);
+ }
+ else if( &rButton == mxTemplateButton.get() )
+ {
+ mxAllRecentThumbnails->Hide();
+ initializeLocalView();
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::NONE));
+ mxLocalView->Show();
+ mxLocalView->reload();
+ mxLocalView->GrabFocus();
+ mxRecentButton->set_active(false);
+ mxTemplateButton->set_active(true);
+ }
+}
+
+IMPL_LINK (BackingWindow, MenuSelectHdl, const OString&, rId, void)
+{
+ if (rId == "clear_all")
+ {
+ SvtHistoryOptions::Clear(EHistoryType::PickList);
+ mxAllRecentThumbnails->Reload();
+ return;
+ }
+ else if (!rId.isEmpty())
+ {
+ initializeLocalView();
+
+ if( rId == "filter_writer" )
+ {
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::WRITER));
+ }
+ else if( rId == "filter_calc" )
+ {
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::CALC));
+ }
+ else if( rId == "filter_impress" )
+ {
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::IMPRESS));
+ }
+ else if( rId == "filter_draw" )
+ {
+ mxLocalView->filterItems(ViewFilter_Application(FILTER_APPLICATION::DRAW));
+ }
+ else if( rId == "manage" )
+ {
+ Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY );
+
+ dispatchURL( ".uno:NewDoc", OUString(), xFrame, { comphelper::makePropertyValue("Referer", OUString("private:user")) } );
+ return;
+ }
+
+ mxAllRecentThumbnails->Hide();
+ mxLocalView->Show();
+ mxLocalView->reload();
+ mxLocalView->GrabFocus();
+ mxRecentButton->set_active(false);
+ mxTemplateButton->set_active(true);
+ }
+}
+
+IMPL_LINK(BackingWindow, CreateContextMenuHdl, ThumbnailViewItem*, pItem, void)
+{
+ const TemplateViewItem *pViewItem = dynamic_cast<TemplateViewItem*>(pItem);
+
+ if (pViewItem)
+ mxLocalView->createContextMenu();
+}
+
+IMPL_LINK(BackingWindow, OpenTemplateHdl, ThumbnailViewItem*, pItem, void)
+{
+ uno::Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue("AsTemplate", true),
+ comphelper::makePropertyValue("MacroExecutionMode", MacroExecMode::USE_CONFIG),
+ comphelper::makePropertyValue("UpdateDocMode", UpdateDocMode::ACCORDING_TO_CONFIG),
+ comphelper::makePropertyValue("InteractionHandler", task::InteractionHandler::createWithParent( ::comphelper::getProcessComponentContext(), nullptr ))
+ };
+
+ TemplateViewItem *pTemplateItem = static_cast<TemplateViewItem*>(pItem);
+
+ Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY );
+
+ try
+ {
+ dispatchURL( pTemplateItem->getPath(), "_default", xFrame, aArgs );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+IMPL_LINK(BackingWindow, EditTemplateHdl, ThumbnailViewItem*, pItem, void)
+{
+ uno::Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue("AsTemplate", false),
+ comphelper::makePropertyValue("MacroExecutionMode", MacroExecMode::USE_CONFIG),
+ comphelper::makePropertyValue("UpdateDocMode", UpdateDocMode::ACCORDING_TO_CONFIG),
+ };
+
+ TemplateViewItem *pViewItem = static_cast<TemplateViewItem*>(pItem);
+
+ Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY );
+
+ try
+ {
+ dispatchURL( pViewItem->getPath(), "_default", xFrame, aArgs );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+namespace {
+
+struct ImplDelayedDispatch
+{
+ Reference< XDispatch > xDispatch;
+ css::util::URL aDispatchURL;
+ Sequence< PropertyValue > aArgs;
+
+ ImplDelayedDispatch( const Reference< XDispatch >& i_xDispatch,
+ const css::util::URL& i_rURL,
+ const Sequence< PropertyValue >& i_rArgs )
+ : xDispatch( i_xDispatch ),
+ aDispatchURL( i_rURL ),
+ aArgs( i_rArgs )
+ {
+ }
+};
+
+}
+
+static void implDispatchDelayed( void*, void* pArg )
+{
+ struct ImplDelayedDispatch* pDispatch = static_cast<ImplDelayedDispatch*>(pArg);
+ try
+ {
+ pDispatch->xDispatch->dispatch( pDispatch->aDispatchURL, pDispatch->aArgs );
+ }
+ catch (const Exception&)
+ {
+ }
+
+ // clean up
+ delete pDispatch;
+}
+
+void BackingWindow::dispatchURL( const OUString& i_rURL,
+ const OUString& rTarget,
+ const Reference< XDispatchProvider >& i_xProv,
+ const Sequence< PropertyValue >& i_rArgs )
+{
+ // if no special dispatch provider is given, get the desktop
+ Reference< XDispatchProvider > xProvider( i_xProv.is() ? i_xProv : mxDesktopDispatchProvider );
+
+ // check for dispatch provider
+ if( !xProvider.is())
+ return;
+
+ // get a URL transformer to clean up the URL
+ css::util::URL aDispatchURL;
+ aDispatchURL.Complete = i_rURL;
+
+ Reference < css::util::XURLTransformer > xURLTransformer(
+ css::util::URLTransformer::create( comphelper::getProcessComponentContext() ) );
+ try
+ {
+ // clean up the URL
+ xURLTransformer->parseStrict( aDispatchURL );
+ // get a Dispatch for the URL and target
+ Reference< XDispatch > xDispatch(
+ xProvider->queryDispatch( aDispatchURL, rTarget, 0 )
+ );
+ // dispatch the URL
+ if ( xDispatch.is() )
+ {
+ std::unique_ptr<ImplDelayedDispatch> pDisp(new ImplDelayedDispatch( xDispatch, aDispatchURL, i_rArgs ));
+ if( Application::PostUserEvent( Link<void*,void>( nullptr, implDispatchDelayed ), pDisp.get() ) )
+ pDisp.release();
+ }
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+}
+
+void BackingWindow::clearRecentFileList()
+{
+ mxAllRecentThumbnails->Clear();
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab:*/
diff --git a/sfx2/source/dialog/backingwindow.hxx b/sfx2/source/dialog/backingwindow.hxx
new file mode 100644
index 000000000..358055c66
--- /dev/null
+++ b/sfx2/source/dialog/backingwindow.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_DIALOG_BACKINGWINDOW_HXX
+#define INCLUDED_SFX2_SOURCE_DIALOG_BACKINGWINDOW_HXX
+
+#include <rtl/ustring.hxx>
+
+#include <vcl/InterimItemWindow.hxx>
+
+#include <recentdocsview.hxx>
+#include <templatedefaultview.hxx>
+
+#include <svtools/acceleratorexecute.hxx>
+
+#include <com/sun/star/datatransfer/dnd/XDropTargetListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <memory>
+
+class BrandImage;
+
+class BackingWindow : public InterimItemWindow
+{
+ css::uno::Reference<css::uno::XComponentContext> mxContext;
+ css::uno::Reference<css::frame::XDispatchProvider> mxDesktopDispatchProvider;
+ css::uno::Reference<css::frame::XFrame> mxFrame;
+
+ /** helper for drag&drop. */
+ css::uno::Reference<css::datatransfer::dnd::XDropTargetListener> mxDropTargetListener;
+
+ std::unique_ptr<weld::Button> mxOpenButton;
+ std::unique_ptr<weld::MenuToggleButton> mxRecentButton;
+ std::unique_ptr<weld::Button> mxRemoteButton;
+ std::unique_ptr<weld::MenuToggleButton> mxTemplateButton;
+
+ std::unique_ptr<weld::Label> mxCreateLabel;
+ std::unique_ptr<weld::Label> mxAltHelpLabel;
+
+ std::unique_ptr<weld::Button> mxWriterAllButton;
+ std::unique_ptr<weld::Button> mxCalcAllButton;
+ std::unique_ptr<weld::Button> mxImpressAllButton;
+ std::unique_ptr<weld::Button> mxDrawAllButton;
+ std::unique_ptr<weld::Button> mxDBAllButton;
+ std::unique_ptr<weld::Button> mxMathAllButton;
+ std::unique_ptr<BrandImage> mxBrandImage;
+ std::unique_ptr<weld::CustomWeld> mxBrandImageWeld;
+
+ std::unique_ptr<weld::Button> mxHelpButton;
+ std::unique_ptr<weld::Button> mxExtensionsButton;
+
+ std::unique_ptr<weld::Container> mxAllButtonsBox;
+ std::unique_ptr<weld::Container> mxButtonsBox;
+ std::unique_ptr<weld::Container> mxSmallButtonsBox;
+
+ std::unique_ptr<sfx2::RecentDocsView> mxAllRecentThumbnails;
+ std::unique_ptr<weld::CustomWeld> mxAllRecentThumbnailsWin;
+ std::unique_ptr<TemplateDefaultView> mxLocalView;
+ std::unique_ptr<weld::CustomWeld> mxLocalViewWin;
+ bool mbLocalViewInitialized;
+
+ css::uno::Reference<css::datatransfer::dnd::XDropTarget> mxDropTarget;
+
+ bool mbInitControls;
+ std::unique_ptr<svt::AcceleratorExecute> mpAccExec;
+
+ void dispatchURL(const OUString& i_rURL, const OUString& i_rTarget = OUString("_default"),
+ const css::uno::Reference<css::frame::XDispatchProvider>& i_xProv
+ = css::uno::Reference<css::frame::XDispatchProvider>(),
+ const css::uno::Sequence<css::beans::PropertyValue>& = css::uno::Sequence<
+ css::beans::PropertyValue>());
+
+ DECL_LINK(ClickHdl, weld::Button&, void);
+ DECL_LINK(ClickHelpHdl, weld::Button&, void);
+ DECL_LINK(MenuSelectHdl, const OString&, void);
+ DECL_LINK(ExtLinkClickHdl, weld::Button&, void);
+ DECL_LINK(CreateContextMenuHdl, ThumbnailViewItem*, void);
+ DECL_LINK(OpenTemplateHdl, ThumbnailViewItem*, void);
+ DECL_LINK(EditTemplateHdl, ThumbnailViewItem*, void);
+
+ void initControls();
+
+ void initializeLocalView();
+
+ void checkInstalledModules();
+
+ void DataChanged(const DataChangedEvent&) override;
+
+ template <typename WidgetClass> void setLargerFont(WidgetClass&, const vcl::Font&);
+ void ApplyStyleSettings();
+
+public:
+ explicit BackingWindow(vcl::Window* pParent);
+ virtual ~BackingWindow() override;
+ virtual void dispose() override;
+
+ virtual bool PreNotify(NotifyEvent& rNEvt) override;
+ virtual void GetFocus() override;
+
+ void setOwningFrame(const css::uno::Reference<css::frame::XFrame>& xFrame);
+
+ void clearRecentFileList();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/basedlgs.cxx b/sfx2/source/dialog/basedlgs.cxx
new file mode 100644
index 000000000..0604f035b
--- /dev/null
+++ b/sfx2/source/dialog/basedlgs.cxx
@@ -0,0 +1,329 @@
+/* -*- 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 <vcl/help.hxx>
+#include <svl/eitem.hxx>
+#include <unotools/viewoptions.hxx>
+#include <vcl/idle.hxx>
+
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/tabdlg.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/viewsh.hxx>
+#include <workwin.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral USERITEM_NAME = u"UserItem";
+
+class SfxModelessDialog_Impl : public SfxListener
+{
+public:
+ OString aWinState;
+ SfxChildWindow* pMgr;
+ bool bClosing;
+ void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ Idle aMoveIdle { "SfxModelessDialog_Impl aMoveIdle" };
+};
+
+void SfxModelessDialog_Impl::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if (pMgr && rHint.GetId() == SfxHintId::Dying) {
+ pMgr->Destroy();
+ }
+}
+
+void SfxModelessDialogController::Initialize(SfxChildWinInfo const *pInfo)
+
+/* [Description]
+
+ Initialization of the class SfxModelessDialog via a SfxChildWinInfo.
+ The initialization is done only in a 2nd step after the constructor, this
+ constructor should be called from the derived class or from the
+ SfxChildWindows.
+*/
+
+{
+ if (!pInfo)
+ return;
+ m_xImpl->aWinState = pInfo->aWinState;
+ if (m_xImpl->aWinState.isEmpty())
+ return;
+ m_xDialog->set_window_state(m_xImpl->aWinState);
+}
+
+SfxModelessDialogController::SfxModelessDialogController(SfxBindings* pBindinx,
+ SfxChildWindow *pCW, weld::Window *pParent, const OUString& rUIXMLDescription,
+ const OString& rID)
+ : SfxDialogController(pParent, rUIXMLDescription, rID)
+{
+ Init(pBindinx, pCW);
+}
+
+/* [Description]
+
+ Fills a SfxChildWinInfo with specific data from SfxModelessDialog,
+ so that it can be written in the INI file. It is assumed that rinfo
+ receives all other possible relevant data in the ChildWindow class.
+ ModelessDialogs have no specific information, so that the base
+ implementation does nothing and therefore must not be called.
+*/
+void SfxModelessDialogController::FillInfo(SfxChildWinInfo& rInfo) const
+{
+ rInfo.aSize = m_xDialog->get_size();
+}
+
+void SfxModelessDialogController::Init(SfxBindings *pBindinx, SfxChildWindow *pCW)
+{
+ m_pBindings = pBindinx;
+ m_xImpl.reset(new SfxModelessDialog_Impl);
+ m_xImpl->pMgr = pCW;
+ m_xImpl->bClosing = false;
+ if (pBindinx)
+ m_xImpl->StartListening( *pBindinx );
+}
+
+/* [Description]
+
+ If a ModelessDialog is enabled its ViewFrame will be activated.
+ This is necessary by PluginInFrames.
+*/
+IMPL_LINK_NOARG(SfxDialogController, FocusChangeHdl, weld::Container&, void)
+{
+ if (m_xDialog->has_toplevel_focus())
+ Activate();
+ else
+ Deactivate();
+}
+
+void SfxModelessDialogController::Activate()
+{
+ if (!m_xImpl || !m_xImpl->pMgr)
+ return;
+ m_pBindings->SetActiveFrame(m_xImpl->pMgr->GetFrame());
+ m_xImpl->pMgr->Activate_Impl();
+}
+
+void SfxModelessDialogController::Deactivate()
+{
+ if (!m_xImpl)
+ return;
+ m_pBindings->SetActiveFrame(css::uno::Reference< css::frame::XFrame>());
+}
+
+SfxModelessDialogController::~SfxModelessDialogController()
+{
+ if (!m_xImpl->pMgr)
+ return;
+ auto xFrame = m_xImpl->pMgr->GetFrame();
+ if (!xFrame)
+ return;
+ if (xFrame == m_pBindings->GetActiveFrame())
+ m_pBindings->SetActiveFrame(nullptr);
+}
+
+void SfxDialogController::EndDialog(int nResponse)
+{
+ if (!m_xDialog->get_visible())
+ return;
+ response(nResponse);
+}
+
+bool SfxModelessDialogController::IsClosing() const
+{
+ return m_xImpl->bClosing;
+}
+
+void SfxModelessDialogController::EndDialog(int nResponse)
+{
+ if (m_xImpl->bClosing)
+ return;
+ // In the case of async dialogs, the call to SfxDialogController::EndDialog
+ // may delete this object, so keep myself alive for the duration of this
+ // stack frame.
+ auto aHoldSelf = shared_from_this();
+ m_xImpl->bClosing = true;
+ SfxDialogController::EndDialog(nResponse);
+ if (!m_xImpl)
+ return;
+ m_xImpl->bClosing = false;
+}
+
+void SfxModelessDialogController::ChildWinDispose()
+{
+ if (m_xImpl->pMgr)
+ {
+ WindowStateMask nMask = WindowStateMask::Pos | WindowStateMask::State;
+ if (m_xDialog->get_resizable())
+ nMask |= WindowStateMask::Size;
+ m_xImpl->aWinState = m_xDialog->get_window_state(nMask);
+ GetBindings().GetWorkWindow_Impl()->ConfigChild_Impl( SfxChildIdentifier::DOCKINGWINDOW, SfxDockingConfig::ALIGNDOCKINGWINDOW, m_xImpl->pMgr->GetType() );
+ }
+
+ m_xImpl->pMgr = nullptr;
+}
+
+/* [Description]
+
+ The window is closed when the ChildWindow is destroyed by running the
+ ChildWindow-slots.
+*/
+void SfxModelessDialogController::Close()
+{
+ if (m_xImpl->bClosing)
+ return;
+ // Execute with Parameters, since Toggle is ignored by some ChildWindows.
+ SfxBoolItem aValue(m_xImpl->pMgr->GetType(), false);
+ m_pBindings->GetDispatcher_Impl()->ExecuteList(
+ m_xImpl->pMgr->GetType(),
+ SfxCallMode::RECORD|SfxCallMode::SYNCHRON, { &aValue } );
+ SfxDialogController::Close();
+}
+
+SfxDialogController::SfxDialogController(weld::Widget* pParent, const OUString& rUIFile,
+ const OString& rDialogId)
+ : GenericDialogController(pParent, rUIFile, rDialogId,
+ comphelper::LibreOfficeKit::isActive()
+ && SfxViewShell::Current()
+ && SfxViewShell::Current()->isLOKMobilePhone())
+{
+ m_xDialog->SetInstallLOKNotifierHdl(LINK(this, SfxDialogController, InstallLOKNotifierHdl));
+ m_xDialog->connect_container_focus_changed(LINK(this, SfxDialogController, FocusChangeHdl));
+}
+
+void SfxDialogController::Close()
+{
+ // tdf3146571 ignore focus changes after we've closed
+ m_xDialog->connect_container_focus_changed(Link<weld::Container&, void>());
+}
+
+IMPL_STATIC_LINK_NOARG(SfxDialogController, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return SfxViewShell::Current();
+}
+
+SfxSingleTabDialogController::SfxSingleTabDialogController(weld::Widget *pParent, const SfxItemSet* pSet,
+ const OUString& rUIXMLDescription, const OString& rID)
+ : SfxOkDialogController(pParent, rUIXMLDescription, rID)
+ , m_pInputSet(pSet)
+ , m_xContainer(m_xDialog->weld_content_area())
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , m_xHelpBtn(m_xBuilder->weld_button("help"))
+{
+ m_xOKBtn->connect_clicked(LINK(this, SfxSingleTabDialogController, OKHdl_Impl));
+}
+
+SfxSingleTabDialogController::~SfxSingleTabDialogController()
+{
+}
+
+/* [Description]
+
+ Insert a (new) TabPage; an existing page is deleted.
+ The passed on page is initialized with the initially given Itemset
+ through calling Reset().
+*/
+void SfxSingleTabDialogController::SetTabPage(std::unique_ptr<SfxTabPage> xTabPage)
+{
+ m_xSfxPage = std::move(xTabPage);
+ if (!m_xSfxPage)
+ return;
+
+ // First obtain the user data, only then Reset()
+ OUString sConfigId = OStringToOUString(m_xSfxPage->GetConfigId(), RTL_TEXTENCODING_UTF8);
+ SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
+ Any aUserItem = aPageOpt.GetUserItem( USERITEM_NAME );
+ OUString sUserData;
+ aUserItem >>= sUserData;
+ m_xSfxPage->SetUserData(sUserData);
+ m_xSfxPage->Reset(GetInputItemSet());
+
+ m_xHelpBtn->set_visible(Help::IsContextHelpEnabled());
+
+ // Set TabPage text in the Dialog if there is any
+ OUString sTitle(m_xSfxPage->GetPageTitle());
+ if (!sTitle.isEmpty())
+ m_xDialog->set_title(sTitle);
+
+ // Dialog receives the HelpId of TabPage if there is any
+ OString sHelpId(m_xSfxPage->GetHelpId());
+ if (!sHelpId.isEmpty())
+ m_xDialog->set_help_id(sHelpId);
+}
+
+/* [Description]
+
+ Ok_Handler; FillItemSet() is called for setting of Page.
+*/
+IMPL_LINK_NOARG(SfxSingleTabDialogController, OKHdl_Impl, weld::Button&, void)
+{
+ const SfxItemSet* pInputSet = GetInputItemSet();
+ if (!pInputSet)
+ {
+ // TabPage without ItemSet
+ m_xDialog->response(RET_OK);
+ return;
+ }
+
+ if (!GetOutputItemSet())
+ {
+ CreateOutputItemSet(*pInputSet);
+ }
+
+ bool bModified = false;
+
+ if (m_xSfxPage->HasExchangeSupport())
+ {
+ DeactivateRC nRet = m_xSfxPage->DeactivatePage(m_xOutputSet.get());
+ if (nRet != DeactivateRC::LeavePage)
+ return;
+ else
+ bModified = m_xOutputSet->Count() > 0;
+ }
+ else
+ bModified = m_xSfxPage->FillItemSet(m_xOutputSet.get());
+
+ if (bModified)
+ {
+ // Save user data in IniManager.
+ m_xSfxPage->FillUserData();
+ OUString sData(m_xSfxPage->GetUserData());
+
+ OUString sConfigId = OStringToOUString(m_xSfxPage->GetConfigId(),
+ RTL_TEXTENCODING_UTF8);
+ SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
+ aPageOpt.SetUserItem( USERITEM_NAME, Any( sData ) );
+ m_xDialog->response(RET_OK);
+ }
+ else
+ m_xDialog->response(RET_CANCEL);
+}
+
+void SfxSingleTabDialogController::CreateOutputItemSet(const SfxItemSet& rSet)
+{
+ assert(!m_xOutputSet && "Double creation of OutputSet!");
+ m_xOutputSet.reset(new SfxItemSet(rSet));
+ m_xOutputSet->ClearItem();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/bluthsnd.cxx b/sfx2/source/dialog/bluthsnd.cxx
new file mode 100644
index 000000000..0083f0749
--- /dev/null
+++ b/sfx2/source/dialog/bluthsnd.cxx
@@ -0,0 +1,50 @@
+/* -*- 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/.
+ */
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <stdio.h>
+
+#include <bluthsndapi.hxx>
+
+SfxBluetoothModel::SendMailResult SfxBluetoothModel::SaveAndSend( const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ SaveResult eSaveResult;
+ SendMailResult eResult = SEND_MAIL_ERROR;
+ OUString aFileName;
+
+ eSaveResult = SaveDocumentAsFormat( OUString(), xFrame, OUString(), aFileName );
+ if( eSaveResult == SAVE_SUCCESSFUL )
+ {
+ maAttachedDocuments.push_back( aFileName );
+ return Send();
+ }
+ else if( eSaveResult == SAVE_CANCELLED )
+ eResult = SEND_MAIL_CANCELLED;
+
+ return eResult;
+}
+
+SfxBluetoothModel::SendMailResult SfxBluetoothModel::Send()
+{
+#ifndef LINUX
+ (void) this; // avoid loplugin:staticmethods
+ return SEND_MAIL_ERROR;
+#else
+ char bthsend[300];
+ SendMailResult eResult = SEND_MAIL_OK;
+ OUString aFileName = maAttachedDocuments[0];
+ snprintf(bthsend,300,"bluetooth-sendto %s",OUStringToOString( aFileName, RTL_TEXTENCODING_UTF8).getStr() );
+ if( !system( bthsend ) )
+ eResult = SEND_MAIL_ERROR;
+ return eResult;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/charmappopup.cxx b/sfx2/source/dialog/charmappopup.cxx
new file mode 100644
index 000000000..93cfa1adb
--- /dev/null
+++ b/sfx2/source/dialog/charmappopup.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 <charmappopup.hxx>
+#include <charmapcontrol.hxx>
+#include <vcl/toolbox.hxx>
+
+CharmapPopup::CharmapPopup(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+CharmapPopup::~CharmapPopup() {}
+
+void CharmapPopup::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ PopupWindowController::initialize(rArguments);
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId));
+}
+
+std::unique_ptr<WeldToolbarPopup> CharmapPopup::weldPopupWindow()
+{
+ return std::make_unique<SfxCharmapCtrl>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> CharmapPopup::createVclPopupWindow(vcl::Window* pParent)
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(
+ getFrameInterface(), pParent,
+ std::make_unique<SfxCharmapCtrl>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString CharmapPopup::getImplementationName()
+{
+ return "com.sun.star.comp.sfx2.InsertSymbolToolBoxControl";
+}
+
+css::uno::Sequence<OUString> CharmapPopup::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_sfx2_InsertSymbolToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new CharmapPopup(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/checkin.cxx b/sfx2/source/dialog/checkin.cxx
new file mode 100644
index 000000000..6d90be8af
--- /dev/null
+++ b/sfx2/source/dialog/checkin.cxx
@@ -0,0 +1,40 @@
+/* -*- 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/.
+ */
+
+#include <checkin.hxx>
+
+SfxCheckinDialog::SfxCheckinDialog(weld::Window* pParent)
+ : GenericDialogController( pParent, "sfx/ui/checkin.ui", "CheckinDialog")
+ , m_xCommentED(m_xBuilder->weld_text_view("VersionComment"))
+ , m_xMajorCB(m_xBuilder->weld_check_button("MajorVersion"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+{
+ m_xOKBtn->connect_clicked(LINK(this, SfxCheckinDialog, OKHdl));
+}
+
+SfxCheckinDialog::~SfxCheckinDialog()
+{
+}
+
+OUString SfxCheckinDialog::GetComment( ) const
+{
+ return m_xCommentED->get_text();
+}
+
+bool SfxCheckinDialog::IsMajor( ) const
+{
+ return m_xMajorCB->get_active();
+}
+
+IMPL_LINK_NOARG(SfxCheckinDialog, OKHdl, weld::Button&, void )
+{
+ m_xDialog->response(RET_OK);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/dialoghelper.cxx b/sfx2/source/dialog/dialoghelper.cxx
new file mode 100644
index 000000000..9585c8baa
--- /dev/null
+++ b/sfx2/source/dialog/dialoghelper.cxx
@@ -0,0 +1,48 @@
+/* -*- 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/.
+ */
+
+#include <unotools/localedatawrapper.hxx>
+#include <sfx2/dialoghelper.hxx>
+#include <tools/datetime.hxx>
+#include <vcl/outdev.hxx>
+
+Size getParagraphPreviewOptimalSize(const OutputDevice& rReference)
+{
+ return rReference.LogicToPixel(Size(68, 112), MapMode(MapUnit::MapAppFont));
+}
+
+Size getDrawPreviewOptimalSize(const OutputDevice& rReference)
+{
+ return rReference.LogicToPixel(Size(88, 42), MapMode(MapUnit::MapAppFont));
+}
+
+Size getPreviewStripSize(const OutputDevice& rReference)
+{
+ return rReference.LogicToPixel(Size(70, 40), MapMode(MapUnit::MapAppFont));
+}
+
+Size getPreviewOptionsSize(const OutputDevice& rReference)
+{
+ return rReference.LogicToPixel(Size(70, 27), MapMode(MapUnit::MapAppFont));
+}
+
+OUString getWidestDateTime(const LocaleDataWrapper& rWrapper, bool bWithSec)
+{
+ Date aDate(22, 12, 2000);
+ tools::Time aTime(22, 59, 59);
+ DateTime aDateTime(aDate, aTime);
+ return formatDateTime(aDateTime, rWrapper, bWithSec);
+}
+
+OUString formatDateTime(const DateTime& rDateTime, const LocaleDataWrapper& rWrapper, bool bWithSec)
+{
+ return rWrapper.getDate(rDateTime) + " " + rWrapper.getTime(rDateTime, bWithSec);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/dinfdlg.cxx b/sfx2/source/dialog/dinfdlg.cxx
new file mode 100644
index 000000000..ddc272907
--- /dev/null
+++ b/sfx2/source/dialog/dinfdlg.cxx
@@ -0,0 +1,2446 @@
+/* -*- 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 <svl/eitem.hxx>
+#include <tools/datetime.hxx>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#include <unotools/datetime.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/stl_types.hxx>
+#include <comphelper/xmlsechelper.hxx>
+#include <unotools/useroptions.hxx>
+#include <svtools/ctrlbox.hxx>
+#include <svtools/imagemgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+
+#include <memory>
+
+#include <comphelper/sequence.hxx>
+#include <comphelper/string.hxx>
+#include <com/sun/star/security/DocumentSignatureInformation.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <unotools/syslocale.hxx>
+#include <rtl/math.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTimeWithTimezone.hpp>
+#include <com/sun/star/util/DateWithTimezone.hpp>
+#include <com/sun/star/util/Duration.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/CmisProperty.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <vcl/timer.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/dinfdlg.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <helper.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <documentfontsdialog.hxx>
+#include <dinfdlg.hrc>
+#include <sfx2/strings.hrc>
+#include <strings.hxx>
+#include <tools/diagnose_ex.h>
+#include "securitypage.hxx"
+
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+struct CustomProperty
+{
+ OUString m_sName;
+ css::uno::Any m_aValue;
+
+ CustomProperty( const OUString& sName, const css::uno::Any& rValue ) :
+ m_sName( sName ), m_aValue( rValue ) {}
+
+ bool operator==(const CustomProperty& rProp) const
+ {
+ return m_sName == rProp.m_sName && m_aValue == rProp.m_aValue;
+ }
+};
+
+SfxPoolItem* SfxDocumentInfoItem::CreateDefault() { return new SfxDocumentInfoItem; }
+
+namespace {
+
+OUString CreateSizeText( sal_Int64 nSize )
+{
+ OUString aUnitStr = " " + SfxResId(STR_BYTES);
+ sal_Int64 nSize1 = nSize;
+ sal_Int64 nSize2 = nSize1;
+ sal_Int64 nMega = 1024 * 1024;
+ sal_Int64 nGiga = nMega * 1024;
+ double fSize = nSize;
+ int nDec = 0;
+
+ if ( nSize1 >= 10000 && nSize1 < nMega )
+ {
+ nSize1 /= 1024;
+ aUnitStr = " " + SfxResId(STR_KB);
+ fSize /= 1024;
+ nDec = 0;
+ }
+ else if ( nSize1 >= nMega && nSize1 < nGiga )
+ {
+ nSize1 /= nMega;
+ aUnitStr = " " + SfxResId(STR_MB);
+ fSize /= nMega;
+ nDec = 2;
+ }
+ else if ( nSize1 >= nGiga )
+ {
+ nSize1 /= nGiga;
+ aUnitStr = " " + SfxResId(STR_GB);
+ fSize /= nGiga;
+ nDec = 3;
+ }
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleWrapper = aSysLocale.GetLocaleData();
+ OUString aSizeStr = rLocaleWrapper.getNum( nSize1, 0 ) + aUnitStr;
+ if ( nSize1 < nSize2 )
+ {
+ aSizeStr = ::rtl::math::doubleToUString( fSize,
+ rtl_math_StringFormat_F, nDec,
+ rLocaleWrapper.getNumDecimalSep()[0] )
+ + aUnitStr
+ + " ("
+ + rLocaleWrapper.getNum( nSize2, 0 )
+ + " "
+ + SfxResId(STR_BYTES)
+ + ")";
+ }
+ return aSizeStr;
+}
+
+OUString ConvertDateTime_Impl( std::u16string_view rName,
+ const util::DateTime& uDT, const LocaleDataWrapper& rWrapper )
+{
+ Date aD(uDT);
+ tools::Time aT(uDT);
+ static const OUStringLiteral aDelim( u", " );
+ OUString aStr = rWrapper.getDate( aD )
+ + aDelim
+ + rWrapper.getTime( aT );
+ std::u16string_view aAuthor = comphelper::string::stripStart(rName, ' ');
+ if (!aAuthor.empty())
+ {
+ aStr += aDelim + aAuthor;
+ }
+ return aStr;
+}
+
+}
+
+
+SfxDocumentInfoItem::SfxDocumentInfoItem()
+ : m_AutoloadDelay(0)
+ , m_isAutoloadEnabled(false)
+ , m_EditingCycles(0)
+ , m_EditingDuration(0)
+ , m_bHasTemplate( true )
+ , m_bDeleteUserData( false )
+ , m_bUseUserData( true )
+ , m_bUseThumbnailSave( true )
+{
+}
+
+SfxDocumentInfoItem::SfxDocumentInfoItem( const OUString& rFile,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps,
+ const uno::Sequence<document::CmisProperty>& i_cmisProps,
+ bool bIs, bool _bIs )
+ : SfxStringItem( SID_DOCINFO, rFile )
+ , m_AutoloadDelay( i_xDocProps->getAutoloadSecs() )
+ , m_AutoloadURL( i_xDocProps->getAutoloadURL() )
+ , m_isAutoloadEnabled( (m_AutoloadDelay > 0) || !m_AutoloadURL.isEmpty() )
+ , m_DefaultTarget( i_xDocProps->getDefaultTarget() )
+ , m_TemplateName( i_xDocProps->getTemplateName() )
+ , m_Author( i_xDocProps->getAuthor() )
+ , m_CreationDate( i_xDocProps->getCreationDate() )
+ , m_ModifiedBy( i_xDocProps->getModifiedBy() )
+ , m_ModificationDate( i_xDocProps->getModificationDate() )
+ , m_PrintedBy( i_xDocProps->getPrintedBy() )
+ , m_PrintDate( i_xDocProps->getPrintDate() )
+ , m_EditingCycles( i_xDocProps->getEditingCycles() )
+ , m_EditingDuration( i_xDocProps->getEditingDuration() )
+ , m_Description( i_xDocProps->getDescription() )
+ , m_Keywords( ::comphelper::string::convertCommaSeparated(
+ i_xDocProps->getKeywords()) )
+ , m_Subject( i_xDocProps->getSubject() )
+ , m_Title( i_xDocProps->getTitle() )
+ , m_bHasTemplate( true )
+ , m_bDeleteUserData( false )
+ , m_bUseUserData( bIs )
+ , m_bUseThumbnailSave( _bIs )
+{
+ try
+ {
+ Reference< beans::XPropertyContainer > xContainer = i_xDocProps->getUserDefinedProperties();
+ if ( xContainer.is() )
+ {
+ Reference < beans::XPropertySet > xSet( xContainer, UNO_QUERY );
+ const Sequence< beans::Property > lProps = xSet->getPropertySetInfo()->getProperties();
+ for ( const beans::Property& rProp : lProps )
+ {
+ // "fix" property? => not a custom property => ignore it!
+ if (!(rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE))
+ {
+ SAL_WARN( "sfx.dialog", "non-removable user-defined property?");
+ continue;
+ }
+
+ uno::Any aValue = xSet->getPropertyValue(rProp.Name);
+ AddCustomProperty( rProp.Name, aValue );
+ }
+ }
+
+ // get CMIS properties
+ m_aCmisProperties = i_cmisProps;
+ }
+ catch ( Exception& ) {}
+}
+
+
+SfxDocumentInfoItem::SfxDocumentInfoItem( const SfxDocumentInfoItem& rItem )
+ : SfxStringItem( rItem )
+ , m_AutoloadDelay( rItem.getAutoloadDelay() )
+ , m_AutoloadURL( rItem.getAutoloadURL() )
+ , m_isAutoloadEnabled( rItem.isAutoloadEnabled() )
+ , m_DefaultTarget( rItem.getDefaultTarget() )
+ , m_TemplateName( rItem.getTemplateName() )
+ , m_Author( rItem.getAuthor() )
+ , m_CreationDate( rItem.getCreationDate() )
+ , m_ModifiedBy( rItem.getModifiedBy() )
+ , m_ModificationDate( rItem.getModificationDate() )
+ , m_PrintedBy( rItem.getPrintedBy() )
+ , m_PrintDate( rItem.getPrintDate() )
+ , m_EditingCycles( rItem.getEditingCycles() )
+ , m_EditingDuration( rItem.getEditingDuration() )
+ , m_Description( rItem.getDescription() )
+ , m_Keywords( rItem.getKeywords() )
+ , m_Subject( rItem.getSubject() )
+ , m_Title( rItem.getTitle() )
+ , m_bHasTemplate( rItem.m_bHasTemplate )
+ , m_bDeleteUserData( rItem.m_bDeleteUserData )
+ , m_bUseUserData( rItem.m_bUseUserData )
+ , m_bUseThumbnailSave( rItem.m_bUseThumbnailSave )
+{
+ for (auto const & pOtherProp : rItem.m_aCustomProperties)
+ {
+ AddCustomProperty( pOtherProp->m_sName, pOtherProp->m_aValue );
+ }
+
+ m_aCmisProperties = rItem.m_aCmisProperties;
+}
+
+SfxDocumentInfoItem::~SfxDocumentInfoItem()
+{
+ ClearCustomProperties();
+}
+
+SfxDocumentInfoItem* SfxDocumentInfoItem::Clone( SfxItemPool * ) const
+{
+ return new SfxDocumentInfoItem( *this );
+}
+
+bool SfxDocumentInfoItem::operator==( const SfxPoolItem& rItem) const
+{
+ if (!SfxStringItem::operator==(rItem))
+ return false;
+ const SfxDocumentInfoItem& rInfoItem(static_cast<const SfxDocumentInfoItem&>(rItem));
+
+ return
+ m_AutoloadDelay == rInfoItem.m_AutoloadDelay &&
+ m_AutoloadURL == rInfoItem.m_AutoloadURL &&
+ m_isAutoloadEnabled == rInfoItem.m_isAutoloadEnabled &&
+ m_DefaultTarget == rInfoItem.m_DefaultTarget &&
+ m_Author == rInfoItem.m_Author &&
+ m_CreationDate == rInfoItem.m_CreationDate &&
+ m_ModifiedBy == rInfoItem.m_ModifiedBy &&
+ m_ModificationDate == rInfoItem.m_ModificationDate &&
+ m_PrintedBy == rInfoItem.m_PrintedBy &&
+ m_PrintDate == rInfoItem.m_PrintDate &&
+ m_EditingCycles == rInfoItem.m_EditingCycles &&
+ m_EditingDuration == rInfoItem.m_EditingDuration &&
+ m_Description == rInfoItem.m_Description &&
+ m_Keywords == rInfoItem.m_Keywords &&
+ m_Subject == rInfoItem.m_Subject &&
+ m_Title == rInfoItem.m_Title &&
+ comphelper::ContainerUniquePtrEquals(m_aCustomProperties, rInfoItem.m_aCustomProperties) &&
+ m_aCmisProperties.getLength() == rInfoItem.m_aCmisProperties.getLength();
+}
+
+
+void SfxDocumentInfoItem::resetUserData(const OUString & i_rAuthor)
+{
+ m_Author = i_rAuthor;
+ DateTime now( DateTime::SYSTEM );
+ m_CreationDate = now.GetUNODateTime();
+ m_ModifiedBy = OUString();
+ m_PrintedBy = OUString();
+ m_ModificationDate = util::DateTime();
+ m_PrintDate = util::DateTime();
+ m_EditingDuration = 0;
+ m_EditingCycles = 1;
+}
+
+
+void SfxDocumentInfoItem::UpdateDocumentInfo(
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps,
+ bool i_bDoNotUpdateUserDefined) const
+{
+ if (isAutoloadEnabled()) {
+ i_xDocProps->setAutoloadSecs(getAutoloadDelay());
+ i_xDocProps->setAutoloadURL(getAutoloadURL());
+ } else {
+ i_xDocProps->setAutoloadSecs(0);
+ i_xDocProps->setAutoloadURL(OUString());
+ }
+ i_xDocProps->setDefaultTarget(getDefaultTarget());
+ i_xDocProps->setAuthor(getAuthor());
+ i_xDocProps->setCreationDate(getCreationDate());
+ i_xDocProps->setModifiedBy(getModifiedBy());
+ i_xDocProps->setModificationDate(getModificationDate());
+ i_xDocProps->setPrintedBy(getPrintedBy());
+ i_xDocProps->setPrintDate(getPrintDate());
+ i_xDocProps->setEditingCycles(getEditingCycles());
+ i_xDocProps->setEditingDuration(getEditingDuration());
+ i_xDocProps->setDescription(getDescription());
+ i_xDocProps->setKeywords(
+ ::comphelper::string::convertCommaSeparated(getKeywords()));
+ i_xDocProps->setSubject(getSubject());
+ i_xDocProps->setTitle(getTitle());
+
+ // this is necessary in case of replaying a recorded macro:
+ // in this case, the macro may contain the 4 old user-defined DocumentInfo
+ // fields, but not any of the DocumentInfo properties;
+ // as a consequence, most of the UserDefined properties of the
+ // DocumentProperties would be summarily deleted here, which does not
+ // seem like a good idea.
+ if (i_bDoNotUpdateUserDefined)
+ return;
+
+ try
+ {
+ Reference< beans::XPropertyContainer > xContainer = i_xDocProps->getUserDefinedProperties();
+ Reference < beans::XPropertySet > xSet( xContainer, UNO_QUERY );
+ Reference< beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo();
+ const Sequence< beans::Property > lProps = xSetInfo->getProperties();
+ for ( const beans::Property& rProp : lProps )
+ {
+ if (rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE)
+ {
+ xContainer->removeProperty( rProp.Name );
+ }
+ }
+
+ for (auto const & pProp : m_aCustomProperties)
+ {
+ try
+ {
+ xContainer->addProperty( pProp->m_sName,
+ beans::PropertyAttribute::REMOVABLE, pProp->m_aValue );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "SfxDocumentInfoItem::updateDocumentInfo(): exception while adding custom properties" );
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "SfxDocumentInfoItem::updateDocumentInfo(): exception while removing custom properties" );
+ }
+}
+
+
+void SfxDocumentInfoItem::SetDeleteUserData( bool bSet )
+{
+ m_bDeleteUserData = bSet;
+}
+
+
+void SfxDocumentInfoItem::SetUseUserData( bool bSet )
+{
+ m_bUseUserData = bSet;
+}
+
+void SfxDocumentInfoItem::SetUseThumbnailSave( bool bSet )
+{
+ m_bUseThumbnailSave = bSet;
+}
+
+std::vector< std::unique_ptr<CustomProperty> > SfxDocumentInfoItem::GetCustomProperties() const
+{
+ std::vector< std::unique_ptr<CustomProperty> > aRet;
+ for (auto const & pOtherProp : m_aCustomProperties)
+ {
+ std::unique_ptr<CustomProperty> pProp(new CustomProperty( pOtherProp->m_sName,
+ pOtherProp->m_aValue ));
+ aRet.push_back( std::move(pProp) );
+ }
+
+ return aRet;
+}
+
+void SfxDocumentInfoItem::ClearCustomProperties()
+{
+ m_aCustomProperties.clear();
+}
+
+void SfxDocumentInfoItem::AddCustomProperty( const OUString& sName, const Any& rValue )
+{
+ std::unique_ptr<CustomProperty> pProp(new CustomProperty( sName, rValue ));
+ m_aCustomProperties.push_back( std::move(pProp) );
+}
+
+
+void SfxDocumentInfoItem::SetCmisProperties( const Sequence< document::CmisProperty >& cmisProps)
+{
+ m_aCmisProperties = cmisProps;
+}
+
+bool SfxDocumentInfoItem::QueryValue( Any& rVal, sal_uInt8 nMemberId ) const
+{
+ OUString aValue;
+ sal_Int32 nValue = 0;
+ bool bValue = false;
+ bool bIsInt = false;
+ bool bIsString = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_DOCINFO_USEUSERDATA:
+ bValue = IsUseUserData();
+ break;
+ case MID_DOCINFO_USETHUMBNAILSAVE:
+ bValue = IsUseThumbnailSave();
+ break;
+ case MID_DOCINFO_DELETEUSERDATA:
+ bValue = m_bDeleteUserData;
+ break;
+ case MID_DOCINFO_AUTOLOADENABLED:
+ bValue = isAutoloadEnabled();
+ break;
+ case MID_DOCINFO_AUTOLOADSECS:
+ bIsInt = true;
+ nValue = getAutoloadDelay();
+ break;
+ case MID_DOCINFO_AUTOLOADURL:
+ bIsString = true;
+ aValue = getAutoloadURL();
+ break;
+ case MID_DOCINFO_DEFAULTTARGET:
+ bIsString = true;
+ aValue = getDefaultTarget();
+ break;
+ case MID_DOCINFO_DESCRIPTION:
+ bIsString = true;
+ aValue = getDescription();
+ break;
+ case MID_DOCINFO_KEYWORDS:
+ bIsString = true;
+ aValue = getKeywords();
+ break;
+ case MID_DOCINFO_SUBJECT:
+ bIsString = true;
+ aValue = getSubject();
+ break;
+ case MID_DOCINFO_TITLE:
+ bIsString = true;
+ aValue = getTitle();
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ if ( bIsString )
+ rVal <<= aValue;
+ else if ( bIsInt )
+ rVal <<= nValue;
+ else
+ rVal <<= bValue;
+ return true;
+}
+
+bool SfxDocumentInfoItem::PutValue( const Any& rVal, sal_uInt8 nMemberId )
+{
+ OUString aValue;
+ sal_Int32 nValue=0;
+ bool bValue = false;
+ bool bRet = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_DOCINFO_USEUSERDATA:
+ bRet = (rVal >>= bValue);
+ if ( bRet )
+ SetUseUserData( bValue );
+ break;
+ case MID_DOCINFO_USETHUMBNAILSAVE:
+ bRet = (rVal >>=bValue);
+ if ( bRet )
+ SetUseThumbnailSave( bValue );
+ break;
+ case MID_DOCINFO_DELETEUSERDATA:
+ // QUESTION: deleting user data was done here; seems to be superfluous!
+ bRet = (rVal >>= bValue);
+ if ( bRet )
+ SetDeleteUserData( bValue );
+ break;
+ case MID_DOCINFO_AUTOLOADENABLED:
+ bRet = (rVal >>= bValue);
+ if ( bRet )
+ m_isAutoloadEnabled = bValue;
+ break;
+ case MID_DOCINFO_AUTOLOADSECS:
+ bRet = (rVal >>= nValue);
+ if ( bRet )
+ m_AutoloadDelay = nValue;
+ break;
+ case MID_DOCINFO_AUTOLOADURL:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ m_AutoloadURL = aValue;
+ break;
+ case MID_DOCINFO_DEFAULTTARGET:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ m_DefaultTarget = aValue;
+ break;
+ case MID_DOCINFO_DESCRIPTION:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ setDescription(aValue);
+ break;
+ case MID_DOCINFO_KEYWORDS:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ setKeywords(aValue);
+ break;
+ case MID_DOCINFO_SUBJECT:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ setSubject(aValue);
+ break;
+ case MID_DOCINFO_TITLE:
+ bRet = (rVal >>= aValue);
+ if ( bRet )
+ setTitle(aValue);
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ return bRet;
+}
+
+SfxDocumentDescPage::SfxDocumentDescPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
+ : SfxTabPage(pPage, pController, "sfx/ui/descriptioninfopage.ui", "DescriptionInfoPage", &rItemSet)
+ , m_pInfoItem(nullptr)
+ , m_xTitleEd(m_xBuilder->weld_entry("title"))
+ , m_xThemaEd(m_xBuilder->weld_entry("subject"))
+ , m_xKeywordsEd(m_xBuilder->weld_entry("keywords"))
+ , m_xCommentEd(m_xBuilder->weld_text_view("comments"))
+{
+ m_xCommentEd->set_size_request(m_xKeywordsEd->get_preferred_size().Width(),
+ m_xCommentEd->get_height_rows(16));
+}
+
+SfxDocumentDescPage::~SfxDocumentDescPage()
+{
+}
+
+std::unique_ptr<SfxTabPage> SfxDocumentDescPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rItemSet)
+{
+ return std::make_unique<SfxDocumentDescPage>(pPage, pController, *rItemSet);
+}
+
+bool SfxDocumentDescPage::FillItemSet(SfxItemSet *rSet)
+{
+ // Test whether a change is present
+ const bool bTitleMod = m_xTitleEd->get_value_changed_from_saved();
+ const bool bThemeMod = m_xThemaEd->get_value_changed_from_saved();
+ const bool bKeywordsMod = m_xKeywordsEd->get_value_changed_from_saved();
+ const bool bCommentMod = m_xCommentEd->get_value_changed_from_saved();
+ if ( !( bTitleMod || bThemeMod || bKeywordsMod || bCommentMod ) )
+ {
+ return false;
+ }
+
+ // Generating the output data
+ const SfxDocumentInfoItem* pItem = nullptr;
+ SfxDocumentInfoItem* pInfo = nullptr;
+ const SfxItemSet* pExSet = GetDialogExampleSet();
+
+ if ( pExSet && !(pItem = pExSet->GetItemIfSet( SID_DOCINFO )) )
+ pInfo = m_pInfoItem;
+ else if ( pItem )
+ pInfo = new SfxDocumentInfoItem( *pItem );
+
+ if ( !pInfo )
+ {
+ SAL_WARN( "sfx.dialog", "SfxDocumentDescPage::FillItemSet(): no item found" );
+ return false;
+ }
+
+ if ( bTitleMod )
+ {
+ pInfo->setTitle( m_xTitleEd->get_text() );
+ }
+ if ( bThemeMod )
+ {
+ pInfo->setSubject( m_xThemaEd->get_text() );
+ }
+ if ( bKeywordsMod )
+ {
+ pInfo->setKeywords( m_xKeywordsEd->get_text() );
+ }
+ if ( bCommentMod )
+ {
+ pInfo->setDescription( m_xCommentEd->get_text() );
+ }
+ rSet->Put( *pInfo );
+ if ( pInfo != m_pInfoItem )
+ {
+ delete pInfo;
+ }
+
+ return true;
+}
+
+void SfxDocumentDescPage::Reset(const SfxItemSet *rSet)
+{
+ m_pInfoItem = const_cast<SfxDocumentInfoItem*>(&rSet->Get(SID_DOCINFO));
+
+ m_xTitleEd->set_text(m_pInfoItem->getTitle());
+ m_xThemaEd->set_text(m_pInfoItem->getSubject());
+ m_xKeywordsEd->set_text(m_pInfoItem->getKeywords());
+ m_xCommentEd->set_text(m_pInfoItem->getDescription());
+
+ m_xTitleEd->save_value();
+ m_xThemaEd->save_value();
+ m_xKeywordsEd->save_value();
+ m_xCommentEd->save_value();
+
+ const SfxBoolItem* pROItem = SfxItemSet::GetItem<SfxBoolItem>(rSet, SID_DOC_READONLY, false);
+ if (pROItem && pROItem->GetValue())
+ {
+ m_xTitleEd->set_editable(false);
+ m_xThemaEd->set_editable(false);
+ m_xKeywordsEd->set_editable(false);
+ m_xCommentEd->set_editable(false);
+ }
+}
+
+SfxDocumentPage::SfxDocumentPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
+ : SfxTabPage(pPage, pController, "sfx/ui/documentinfopage.ui", "DocumentInfoPage", &rItemSet)
+ , bEnableUseUserData( false )
+ , bHandleDelete( false )
+ , m_xBmp(m_xBuilder->weld_image("icon"))
+ , m_xNameED(m_xBuilder->weld_label("nameed"))
+ , m_xChangePassBtn(m_xBuilder->weld_button("changepass"))
+ , m_xShowTypeFT(m_xBuilder->weld_label("showtype"))
+ , m_xFileValEd(m_xBuilder->weld_link_button("showlocation"))
+ , m_xShowSizeFT(m_xBuilder->weld_label("showsize"))
+ , m_xCreateValFt(m_xBuilder->weld_label("showcreate"))
+ , m_xChangeValFt(m_xBuilder->weld_label("showmodify"))
+ , m_xSignedValFt(m_xBuilder->weld_label("showsigned"))
+ , m_xSignatureBtn(m_xBuilder->weld_button("signature"))
+ , m_xPrintValFt(m_xBuilder->weld_label("showprint"))
+ , m_xTimeLogValFt(m_xBuilder->weld_label("showedittime"))
+ , m_xDocNoValFt(m_xBuilder->weld_label("showrevision"))
+ , m_xUseUserDataCB(m_xBuilder->weld_check_button("userdatacb"))
+ , m_xDeleteBtn(m_xBuilder->weld_button("reset"))
+ , m_xUseThumbnailSaveCB(m_xBuilder->weld_check_button("thumbnailsavecb"))
+ , m_xTemplFt(m_xBuilder->weld_label("templateft"))
+ , m_xTemplValFt(m_xBuilder->weld_label("showtemplate"))
+ , m_xImagePreferredDpiCheckButton(m_xBuilder->weld_check_button("image-preferred-dpi-checkbutton"))
+ , m_xImagePreferredDpiComboBox(m_xBuilder->weld_combo_box("image-preferred-dpi-combobox"))
+{
+ m_xUseUserDataCB->set_accessible_description(SfxResId(STR_A11Y_DESC_USERDATA));
+
+ m_aUnknownSize = m_xShowSizeFT->get_label();
+ m_xShowSizeFT->set_label(OUString());
+
+ m_aMultiSignedStr = m_xSignedValFt->get_label();
+ m_xSignedValFt->set_label(OUString());
+
+ ImplUpdateSignatures();
+ ImplCheckPasswordState();
+ m_xChangePassBtn->connect_clicked( LINK( this, SfxDocumentPage, ChangePassHdl ) );
+ m_xSignatureBtn->connect_clicked( LINK( this, SfxDocumentPage, SignatureHdl ) );
+ m_xDeleteBtn->connect_clicked( LINK( this, SfxDocumentPage, DeleteHdl ) );
+ m_xImagePreferredDpiCheckButton->connect_toggled(LINK(this, SfxDocumentPage, ImagePreferredDPICheckBoxClicked));
+
+ // [i96288] Check if the document signature command is enabled
+ // on the main list enable/disable the pushbutton accordingly
+ SvtCommandOptions aCmdOptions;
+ if ( aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, "Signature" ) )
+ m_xSignatureBtn->set_sensitive(false);
+}
+
+SfxDocumentPage::~SfxDocumentPage()
+{
+}
+
+IMPL_LINK_NOARG(SfxDocumentPage, DeleteHdl, weld::Button&, void)
+{
+ OUString aName;
+ if (bEnableUseUserData && m_xUseUserDataCB->get_active())
+ aName = SvtUserOptions().GetFullName();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ DateTime now( DateTime::SYSTEM );
+ util::DateTime uDT( now.GetUNODateTime() );
+ m_xCreateValFt->set_label( ConvertDateTime_Impl( aName, uDT, rLocaleWrapper ) );
+ m_xChangeValFt->set_label( "" );
+ m_xPrintValFt->set_label( "" );
+ const tools::Time aTime( 0 );
+ m_xTimeLogValFt->set_label( rLocaleWrapper.getDuration( aTime ) );
+ m_xDocNoValFt->set_label(OUString('1'));
+ bHandleDelete = true;
+}
+
+IMPL_LINK_NOARG(SfxDocumentPage, SignatureHdl, weld::Button&, void)
+{
+ SfxObjectShell* pDoc = SfxObjectShell::Current();
+ if( pDoc )
+ {
+ pDoc->SignDocumentContent(GetFrameWeld());
+
+ ImplUpdateSignatures();
+ }
+}
+
+IMPL_LINK_NOARG(SfxDocumentPage, ImagePreferredDPICheckBoxClicked, weld::Toggleable&, void)
+{
+ bool bEnabled = m_xImagePreferredDpiCheckButton->get_state() == TRISTATE_TRUE;
+ m_xImagePreferredDpiComboBox->set_sensitive(bEnabled);
+}
+
+IMPL_LINK_NOARG(SfxDocumentPage, ChangePassHdl, weld::Button&, void)
+{
+ SfxObjectShell* pShell = SfxObjectShell::Current();
+ do
+ {
+ if (!pShell)
+ break;
+ SfxItemSet* pMedSet = pShell->GetMedium()->GetItemSet();
+ if (!pMedSet)
+ break;
+ std::shared_ptr<const SfxFilter> pFilter = pShell->GetMedium()->GetFilter();
+ if (!pFilter)
+ break;
+
+ sfx2::RequestPassword(pFilter, OUString(), pMedSet, GetFrameWeld()->GetXWindow());
+ pShell->SetModified();
+ }
+ while (false);
+}
+
+void SfxDocumentPage::ImplUpdateSignatures()
+{
+ SfxObjectShell* pDoc = SfxObjectShell::Current();
+ if ( !pDoc )
+ return;
+
+ SfxMedium* pMedium = pDoc->GetMedium();
+ if ( !pMedium || pMedium->GetName().isEmpty() || !pMedium->GetStorage().is() )
+ return;
+
+ Reference< security::XDocumentDigitalSignatures > xD;
+ try
+ {
+ xD = security::DocumentDigitalSignatures::createDefault(comphelper::getProcessComponentContext());
+ xD->setParentWindow(GetDialogController()->getDialog()->GetXWindow());
+ }
+ catch ( const css::uno::DeploymentException& )
+ {
+ }
+ OUString s;
+ Sequence< security::DocumentSignatureInformation > aInfos;
+
+ if ( xD.is() )
+ aInfos = xD->verifyDocumentContentSignatures( pMedium->GetZipStorageToSign_Impl(),
+ uno::Reference< io::XInputStream >() );
+ if ( aInfos.getLength() > 1 )
+ s = m_aMultiSignedStr;
+ else if ( aInfos.getLength() == 1 )
+ {
+ const security::DocumentSignatureInformation& rInfo = aInfos[ 0 ];
+ s = utl::GetDateTimeString( rInfo.SignatureDate, rInfo.SignatureTime ) + ", " +
+ comphelper::xmlsec::GetContentPart(rInfo.Signer->getSubjectName(), rInfo.Signer->getCertificateKind());
+ }
+ m_xSignedValFt->set_label(s);
+}
+
+void SfxDocumentPage::ImplCheckPasswordState()
+{
+ SfxObjectShell* pShell = SfxObjectShell::Current();
+ do
+ {
+ if (!pShell)
+ break;
+ SfxItemSet* pMedSet = pShell->GetMedium()->GetItemSet();
+ if (!pMedSet)
+ break;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pMedSet, SID_ENCRYPTIONDATA, false);
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if (pEncryptionDataItem)
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+ else
+ break;
+
+ if (!aEncryptionData.hasElements())
+ break;
+ m_xChangePassBtn->set_sensitive(true);
+ return;
+ }
+ while (false);
+ m_xChangePassBtn->set_sensitive(false);
+}
+
+std::unique_ptr<SfxTabPage> SfxDocumentPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rItemSet)
+{
+ return std::make_unique<SfxDocumentPage>(pPage, pController, *rItemSet);
+}
+
+void SfxDocumentPage::EnableUseUserData()
+{
+ bEnableUseUserData = true;
+ m_xUseUserDataCB->show();
+ m_xDeleteBtn->show();
+}
+
+bool SfxDocumentPage::FillItemSet( SfxItemSet* rSet )
+{
+ bool bRet = false;
+
+ if ( !bHandleDelete && bEnableUseUserData &&
+ m_xUseUserDataCB->get_state_changed_from_saved() )
+ {
+ const SfxItemSet* pExpSet = GetDialogExampleSet();
+ const SfxDocumentInfoItem* pInfoItem;
+
+ if ( pExpSet && (pInfoItem = pExpSet->GetItemIfSet( SID_DOCINFO ) ) )
+ {
+ bool bUseData = ( TRISTATE_TRUE == m_xUseUserDataCB->get_state() );
+ const_cast<SfxDocumentInfoItem*>(pInfoItem)->SetUseUserData( bUseData );
+ rSet->Put( *pInfoItem );
+ bRet = true;
+ }
+ }
+
+ if ( bHandleDelete )
+ {
+ const SfxItemSet* pExpSet = GetDialogExampleSet();
+ const SfxDocumentInfoItem* pInfoItem;
+ if ( pExpSet && (pInfoItem = pExpSet->GetItemIfSet( SID_DOCINFO )) )
+ {
+ bool bUseAuthor = bEnableUseUserData && m_xUseUserDataCB->get_active();
+ SfxDocumentInfoItem newItem( *pInfoItem );
+ newItem.resetUserData( bUseAuthor
+ ? SvtUserOptions().GetFullName()
+ : OUString() );
+ const_cast<SfxDocumentInfoItem*>(pInfoItem)->SetUseUserData( TRISTATE_TRUE == m_xUseUserDataCB->get_state() );
+ newItem.SetUseUserData( TRISTATE_TRUE == m_xUseUserDataCB->get_state() );
+
+ newItem.SetDeleteUserData( true );
+ rSet->Put( newItem );
+ bRet = true;
+ }
+ }
+
+ if ( m_xUseThumbnailSaveCB->get_state_changed_from_saved() )
+ {
+ const SfxItemSet* pExpSet = GetDialogExampleSet();
+ const SfxDocumentInfoItem* pInfoItem;
+
+ if ( pExpSet && (pInfoItem = pExpSet->GetItemIfSet( SID_DOCINFO )) )
+ {
+ bool bUseThumbnail = ( TRISTATE_TRUE == m_xUseThumbnailSaveCB->get_state() );
+ const_cast<SfxDocumentInfoItem*>(pInfoItem)->SetUseThumbnailSave( bUseThumbnail );
+ rSet->Put( *pInfoItem );
+ bRet = true;
+ }
+ }
+
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if (pDocSh)
+ {
+ uno::Reference<lang::XMultiServiceFactory> xFac(pDocSh->GetModel(), uno::UNO_QUERY);
+ if (xFac.is())
+ {
+ uno::Reference<beans::XPropertySet> xProps(xFac->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
+ if (xProps.is())
+ {
+ sal_Int32 nImagePreferredDPI = 0;
+ if (m_xImagePreferredDpiCheckButton->get_state() == TRISTATE_TRUE)
+ {
+ OUString aImagePreferredDPIString = m_xImagePreferredDpiComboBox->get_active_text();
+ nImagePreferredDPI = aImagePreferredDPIString.toInt32();
+ }
+ xProps->setPropertyValue("ImagePreferredDPI", uno::Any(nImagePreferredDPI));
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void SfxDocumentPage::Reset( const SfxItemSet* rSet )
+{
+ // Determine the document information
+ const SfxDocumentInfoItem& rInfoItem = rSet->Get(SID_DOCINFO);
+
+ // template data
+ if (rInfoItem.HasTemplate())
+ {
+ const OUString& rName = rInfoItem.getTemplateName();
+ if (rName.getLength() > SAL_MAX_INT16) // tdf#122780 pick some ~arbitrary max size
+ m_xTemplValFt->set_label(rName.copy(0, SAL_MAX_INT16));
+ else
+ m_xTemplValFt->set_label(rName);
+ }
+ else
+ {
+ m_xTemplFt->hide();
+ m_xTemplValFt->hide();
+ }
+
+ // determine file name
+ OUString aFile( rInfoItem.GetValue() );
+ OUString aFactory( aFile );
+ if ( aFile.getLength() > 2 && aFile[0] == '[' )
+ {
+ sal_Int32 nPos = aFile.indexOf( ']' );
+ aFactory = aFile.copy( 1, nPos-1 );
+ aFile = aFile.copy( nPos+1 );
+ }
+
+ // determine name
+ INetURLObject aURL(aFile);
+ OUString aName = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ if ( aName.isEmpty() || aURL.GetProtocol() == INetProtocol::PrivSoffice )
+ aName = SfxResId( STR_NONAME );
+ m_xNameED->set_label( aName );
+
+ // determine context symbol
+ aURL.SetSmartProtocol( INetProtocol::File );
+ aURL.SetSmartURL( aFactory);
+ const OUString& rMainURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ OUString aImage = SvFileInformationManager::GetImageId( aURL, true );
+ m_xBmp->set_from_icon_name(aImage);
+
+ // determine size and type
+ OUString aSizeText( m_aUnknownSize );
+ if ( aURL.GetProtocol() == INetProtocol::File ||
+ aURL.isAnyKnownWebDAVScheme() )
+ aSizeText = CreateSizeText( SfxContentHelper::GetSize( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
+ m_xShowSizeFT->set_label( aSizeText );
+
+ OUString aDescription = SvFileInformationManager::GetDescription( INetURLObject(rMainURL) );
+ if ( aDescription.isEmpty() )
+ aDescription = SfxResId( STR_SFX_NEWOFFICEDOC );
+ m_xShowTypeFT->set_label( aDescription );
+
+ // determine location
+ aURL.SetSmartURL( aFile);
+ if ( aURL.GetProtocol() == INetProtocol::File )
+ {
+ INetURLObject aPath( aURL );
+ aPath.setFinalSlash();
+ aPath.removeSegment();
+ // we know it's a folder -> don't need the final slash, but it's better for WB_PATHELLIPSIS
+ aPath.removeFinalSlash();
+ OUString aText( aPath.PathToFileName() ); //! (pb) MaxLen?
+ m_xFileValEd->set_label(aText);
+ OUString aURLStr;
+ osl::FileBase::getFileURLFromSystemPath(aText, aURLStr);
+ m_xFileValEd->set_uri(aURLStr);
+ }
+ else if (aURL.GetProtocol() != INetProtocol::PrivSoffice)
+ {
+ m_xFileValEd->set_label(aURL.GetPartBeforeLastName());
+ m_xFileValEd->set_uri(m_xFileValEd->get_label());
+ }
+
+ // handle access data
+ bool bUseUserData = rInfoItem.IsUseUserData();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ m_xCreateValFt->set_label( ConvertDateTime_Impl( rInfoItem.getAuthor(),
+ rInfoItem.getCreationDate(), rLocaleWrapper ) );
+ util::DateTime aTime( rInfoItem.getModificationDate() );
+ if ( aTime.Month > 0 )
+ m_xChangeValFt->set_label( ConvertDateTime_Impl(
+ rInfoItem.getModifiedBy(), aTime, rLocaleWrapper ) );
+ aTime = rInfoItem.getPrintDate();
+ if ( aTime.Month > 0 )
+ m_xPrintValFt->set_label( ConvertDateTime_Impl( rInfoItem.getPrintedBy(),
+ aTime, rLocaleWrapper ) );
+ const tools::Long nTime = rInfoItem.getEditingDuration();
+ if ( bUseUserData )
+ {
+ const tools::Time aT( nTime/3600, (nTime%3600)/60, nTime%60 );
+ m_xTimeLogValFt->set_label( rLocaleWrapper.getDuration( aT ) );
+ m_xDocNoValFt->set_label( OUString::number(
+ rInfoItem.getEditingCycles() ) );
+ }
+
+ bool bUseThumbnailSave = rInfoItem.IsUseThumbnailSave();
+
+ // Check for cmis properties where otherwise unavailable
+ if ( rInfoItem.isCmisDocument( ) )
+ {
+ const uno::Sequence< document::CmisProperty > aCmisProps = rInfoItem.GetCmisProperties();
+ for ( const auto& rCmisProp : aCmisProps )
+ {
+ if ( rCmisProp.Id == "cmis:contentStreamLength" &&
+ aSizeText == m_aUnknownSize )
+ {
+ Sequence< sal_Int64 > seqValue;
+ rCmisProp.Value >>= seqValue;
+ SvNumberFormatter aNumberFormatter( ::comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetLanguageTag().getLanguageType() );
+ sal_uInt32 nIndex = aNumberFormatter.GetFormatIndex( NF_NUMBER_SYSTEM );
+ if ( seqValue.hasElements() )
+ {
+ OUString sValue;
+ aNumberFormatter.GetInputLineString( seqValue[0], nIndex, sValue );
+ m_xShowSizeFT->set_label( CreateSizeText( sValue.toInt64( ) ) );
+ }
+ }
+
+ util::DateTime uDT;
+ OUString emptyDate = ConvertDateTime_Impl( u"", uDT, rLocaleWrapper );
+ if ( rCmisProp.Id == "cmis:creationDate" &&
+ (m_xCreateValFt->get_label() == emptyDate ||
+ m_xCreateValFt->get_label().isEmpty()))
+ {
+ Sequence< util::DateTime > seqValue;
+ rCmisProp.Value >>= seqValue;
+ if ( seqValue.hasElements() )
+ {
+ m_xCreateValFt->set_label( ConvertDateTime_Impl( u"", seqValue[0], rLocaleWrapper ) );
+ }
+ }
+ if ( rCmisProp.Id == "cmis:lastModificationDate" &&
+ (m_xChangeValFt->get_label() == emptyDate ||
+ m_xChangeValFt->get_label().isEmpty()))
+ {
+ Sequence< util::DateTime > seqValue;
+ rCmisProp.Value >>= seqValue;
+ if ( seqValue.hasElements() )
+ {
+ m_xChangeValFt->set_label( ConvertDateTime_Impl( u"", seqValue[0], rLocaleWrapper ) );
+ }
+ }
+ }
+ }
+
+ m_xUseUserDataCB->set_active(bUseUserData);
+ m_xUseUserDataCB->save_state();
+ m_xUseUserDataCB->set_sensitive( bEnableUseUserData );
+ bHandleDelete = false;
+ m_xDeleteBtn->set_sensitive( bEnableUseUserData );
+ m_xUseThumbnailSaveCB->set_active(bUseThumbnailSave);
+ m_xUseThumbnailSaveCB->save_state();
+
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ sal_Int32 nImagePreferredDPI = 0;
+ if (pDocSh)
+ {
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFac( pDocSh->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW );
+
+ xProps->getPropertyValue("ImagePreferredDPI") >>= nImagePreferredDPI;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ if (nImagePreferredDPI > 0)
+ {
+ m_xImagePreferredDpiCheckButton->set_state(TRISTATE_TRUE);
+ m_xImagePreferredDpiComboBox->set_sensitive(true);
+ m_xImagePreferredDpiComboBox->set_entry_text(OUString::number(nImagePreferredDPI));
+ }
+ else
+ {
+ m_xImagePreferredDpiCheckButton->set_state(TRISTATE_FALSE);
+ m_xImagePreferredDpiComboBox->set_sensitive(false);
+ m_xImagePreferredDpiComboBox->set_entry_text("");
+ }
+
+}
+
+SfxDocumentInfoDialog::SfxDocumentInfoDialog(weld::Window* pParent, const SfxItemSet& rItemSet)
+ : SfxTabDialogController(pParent, "sfx/ui/documentpropertiesdialog.ui",
+ "DocumentPropertiesDialog", &rItemSet)
+{
+ const SfxDocumentInfoItem& rInfoItem = rItemSet.Get( SID_DOCINFO );
+
+#ifdef DBG_UTIL
+ const SfxStringItem* pURLItem = rItemSet.GetItem<SfxStringItem>(SID_BASEURL, false);
+ DBG_ASSERT( pURLItem, "No BaseURL provided for InternetTabPage!" );
+#endif
+
+ // Determine the Titles
+ OUString aTitle(m_xDialog->get_title());
+ const SfxStringItem* pItem = rItemSet.GetItemIfSet( SID_EXPLORER_PROPS_START, false );
+ if ( !pItem )
+ {
+ // File name
+ const OUString& aFile( rInfoItem.GetValue() );
+
+ INetURLObject aURL;
+ aURL.SetSmartProtocol( INetProtocol::File );
+ aURL.SetSmartURL( aFile);
+ if ( INetProtocol::PrivSoffice != aURL.GetProtocol() )
+ {
+ OUString aLastName( aURL.GetLastName() );
+ if ( !aLastName.isEmpty() )
+ aTitle = aTitle.replaceFirst("%1", aLastName);
+ else
+ aTitle = aTitle.replaceFirst("%1", aFile);
+ }
+ else
+ aTitle = aTitle.replaceFirst("%1", SfxResId( STR_NONAME ));
+ }
+ else
+ {
+ aTitle = aTitle.replaceFirst("%1", pItem->GetValue());
+ }
+ m_xDialog->set_title(aTitle);
+
+ // Property Pages
+ AddTabPage("general", SfxDocumentPage::Create, nullptr);
+ AddTabPage("description", SfxDocumentDescPage::Create, nullptr);
+ AddTabPage("customprops", SfxCustomPropertiesPage::Create, nullptr);
+ if (rInfoItem.isCmisDocument())
+ AddTabPage("cmisprops", SfxCmisPropertiesPage::Create, nullptr);
+ else
+ RemoveTabPage("cmisprops");
+ AddTabPage("security", SfxSecurityPage::Create, nullptr);
+}
+
+void SfxDocumentInfoDialog::PageCreated(const OString& rId, SfxTabPage &rPage)
+{
+ if (rId == "general")
+ static_cast<SfxDocumentPage&>(rPage).EnableUseUserData();
+}
+
+void SfxDocumentInfoDialog::AddFontTabPage()
+{
+ AddTabPage("font", SfxResId(STR_FONT_TABPAGE), SfxDocumentFontsPage::Create);
+}
+
+// class CustomPropertiesYesNoButton -------------------------------------
+
+CustomPropertiesYesNoButton::CustomPropertiesYesNoButton(std::unique_ptr<weld::Widget> xTopLevel,
+ std::unique_ptr<weld::RadioButton> xYesButton,
+ std::unique_ptr<weld::RadioButton> xNoButton)
+ : m_xTopLevel(std::move(xTopLevel))
+ , m_xYesButton(std::move(xYesButton))
+ , m_xNoButton(std::move(xNoButton))
+{
+ CheckNo();
+}
+
+CustomPropertiesYesNoButton::~CustomPropertiesYesNoButton()
+{
+}
+
+namespace {
+
+class DurationDialog_Impl : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::CheckButton> m_xNegativeCB;
+ std::unique_ptr<weld::SpinButton> m_xYearNF;
+ std::unique_ptr<weld::SpinButton> m_xMonthNF;
+ std::unique_ptr<weld::SpinButton> m_xDayNF;
+ std::unique_ptr<weld::SpinButton> m_xHourNF;
+ std::unique_ptr<weld::SpinButton> m_xMinuteNF;
+ std::unique_ptr<weld::SpinButton> m_xSecondNF;
+ std::unique_ptr<weld::SpinButton> m_xMSecondNF;
+
+public:
+ DurationDialog_Impl(weld::Widget* pParent, const util::Duration& rDuration);
+ util::Duration GetDuration() const;
+};
+
+}
+
+DurationDialog_Impl::DurationDialog_Impl(weld::Widget* pParent, const util::Duration& rDuration)
+ : GenericDialogController(pParent, "sfx/ui/editdurationdialog.ui", "EditDurationDialog")
+ , m_xNegativeCB(m_xBuilder->weld_check_button("negative"))
+ , m_xYearNF(m_xBuilder->weld_spin_button("years"))
+ , m_xMonthNF(m_xBuilder->weld_spin_button("months"))
+ , m_xDayNF(m_xBuilder->weld_spin_button("days"))
+ , m_xHourNF(m_xBuilder->weld_spin_button("hours"))
+ , m_xMinuteNF(m_xBuilder->weld_spin_button("minutes"))
+ , m_xSecondNF(m_xBuilder->weld_spin_button("seconds"))
+ , m_xMSecondNF(m_xBuilder->weld_spin_button("milliseconds"))
+{
+ m_xNegativeCB->set_active(rDuration.Negative);
+ m_xYearNF->set_value(rDuration.Years);
+ m_xMonthNF->set_value(rDuration.Months);
+ m_xDayNF->set_value(rDuration.Days);
+ m_xHourNF->set_value(rDuration.Hours);
+ m_xMinuteNF->set_value(rDuration.Minutes);
+ m_xSecondNF->set_value(rDuration.Seconds);
+ m_xMSecondNF->set_value(rDuration.NanoSeconds);
+}
+
+util::Duration DurationDialog_Impl::GetDuration() const
+{
+ util::Duration aRet;
+ aRet.Negative = m_xNegativeCB->get_active();
+ aRet.Years = m_xYearNF->get_value();
+ aRet.Months = m_xMonthNF->get_value();
+ aRet.Days = m_xDayNF->get_value();
+ aRet.Hours = m_xHourNF->get_value();
+ aRet.Minutes = m_xMinuteNF->get_value();
+ aRet.Seconds = m_xSecondNF->get_value();
+ aRet.NanoSeconds = m_xMSecondNF->get_value();
+ return aRet;
+}
+
+CustomPropertiesDurationField::CustomPropertiesDurationField(std::unique_ptr<weld::Entry> xEntry,
+ std::unique_ptr<weld::Button> xEditButton)
+ : m_xEntry(std::move(xEntry))
+ , m_xEditButton(std::move(xEditButton))
+{
+ m_xEditButton->connect_clicked(LINK(this, CustomPropertiesDurationField, ClickHdl));
+ SetDuration( util::Duration(false, 0, 0, 0, 0, 0, 0, 0) );
+}
+
+void CustomPropertiesDurationField::set_visible(bool bVisible)
+{
+ m_xEntry->set_visible(bVisible);
+ m_xEditButton->set_visible(bVisible);
+}
+
+void CustomPropertiesDurationField::SetDuration( const util::Duration& rDuration )
+{
+ m_aDuration = rDuration;
+ OUString sText = (rDuration.Negative ? OUString('-') : OUString('+')) +
+ SfxResId(SFX_ST_DURATION_FORMAT);
+ sText = sText.replaceFirst( "%1", OUString::number( rDuration.Years ) );
+ sText = sText.replaceFirst( "%2", OUString::number( rDuration.Months ) );
+ sText = sText.replaceFirst( "%3", OUString::number( rDuration.Days ) );
+ sText = sText.replaceFirst( "%4", OUString::number( rDuration.Hours ) );
+ sText = sText.replaceFirst( "%5", OUString::number( rDuration.Minutes) );
+ sText = sText.replaceFirst( "%6", OUString::number( rDuration.Seconds) );
+ m_xEntry->set_text(sText);
+}
+
+IMPL_LINK(CustomPropertiesDurationField, ClickHdl, weld::Button&, rButton, void)
+{
+ DurationDialog_Impl aDurationDlg(&rButton, GetDuration());
+ if (aDurationDlg.run() == RET_OK)
+ SetDuration(aDurationDlg.GetDuration());
+}
+
+namespace
+{
+ void fillNameBox(weld::ComboBox& rNameBox)
+ {
+ for (size_t i = 0; i < SAL_N_ELEMENTS(SFX_CB_PROPERTY_STRINGARRAY); ++i)
+ rNameBox.append_text(SfxResId(SFX_CB_PROPERTY_STRINGARRAY[i]));
+ Size aSize(rNameBox.get_preferred_size());
+ rNameBox.set_size_request(aSize.Width(), aSize.Height());
+ }
+
+ void fillTypeBox(weld::ComboBox& rTypeBox)
+ {
+ for (size_t i = 0; i < SAL_N_ELEMENTS(SFX_LB_PROPERTY_STRINGARRAY); ++i)
+ {
+ OUString sId(OUString::number(SFX_LB_PROPERTY_STRINGARRAY[i].second));
+ rTypeBox.append(sId, SfxResId(SFX_LB_PROPERTY_STRINGARRAY[i].first));
+ }
+ rTypeBox.set_active(0);
+ Size aSize(rTypeBox.get_preferred_size());
+ rTypeBox.set_size_request(aSize.Width(), aSize.Height());
+ }
+}
+
+// struct CustomPropertyLine ---------------------------------------------
+CustomPropertyLine::CustomPropertyLine(CustomPropertiesWindow* pParent, weld::Widget* pContainer)
+ : m_pParent(pParent)
+ , m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/linefragment.ui"))
+ , m_xLine(m_xBuilder->weld_container("lineentry"))
+ , m_xNameBox(m_xBuilder->weld_combo_box("namebox"))
+ , m_xTypeBox(m_xBuilder->weld_combo_box("typebox"))
+ , m_xValueEdit(m_xBuilder->weld_entry("valueedit"))
+ , m_xDateTimeBox(m_xBuilder->weld_widget("datetimebox"))
+ , m_xDateField(new CustomPropertiesDateField(new SvtCalendarBox(m_xBuilder->weld_menu_button("date"))))
+ , m_xTimeField(new CustomPropertiesTimeField(m_xBuilder->weld_formatted_spin_button("time")))
+ , m_xDurationBox(m_xBuilder->weld_widget("durationbox"))
+ , m_xDurationField(new CustomPropertiesDurationField(m_xBuilder->weld_entry("duration"),
+ m_xBuilder->weld_button("durationbutton")))
+ , m_xYesNoButton(new CustomPropertiesYesNoButton(m_xBuilder->weld_widget("yesno"),
+ m_xBuilder->weld_radio_button("yes"),
+ m_xBuilder->weld_radio_button("no")))
+ , m_xRemoveButton(m_xBuilder->weld_button("remove"))
+ , m_bTypeLostFocus( false )
+{
+ fillNameBox(*m_xNameBox);
+ fillTypeBox(*m_xTypeBox);
+
+ m_xTypeBox->connect_changed(LINK(this, CustomPropertyLine, TypeHdl));
+ m_xRemoveButton->connect_clicked(LINK(this, CustomPropertyLine, RemoveHdl));
+ m_xValueEdit->connect_focus_out(LINK(this, CustomPropertyLine, EditLoseFocusHdl));
+ //add lose focus handlers of date/time fields
+ m_xTypeBox->connect_focus_out(LINK(this, CustomPropertyLine, BoxLoseFocusHdl));
+}
+
+void CustomPropertyLine::Clear()
+{
+ m_xNameBox->set_active(-1);
+ m_xValueEdit->set_text(OUString());
+
+}
+
+void CustomPropertyLine::Hide()
+{
+ m_xLine->hide();
+}
+
+CustomPropertiesWindow::CustomPropertiesWindow(weld::Container& rParent, weld::Label& rHeaderAccName,
+ weld::Label& rHeaderAccType, weld::Label& rHeaderAccValue)
+ : m_nHeight(0)
+ , m_nLineHeight(0)
+ , m_nScrollPos(0)
+ , m_pCurrentLine(nullptr)
+ , m_aNumberFormatter(::comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetLanguageTag().getLanguageType())
+ , m_aEditLoseFocusIdle("sfx2 CustomPropertiesWindow loseFocusIdle")
+ , m_aBoxLoseFocusIdle("sfx2 CustomPropertiesWindow m_aBoxLoseFocusIdle")
+ , m_rBody(rParent)
+ , m_rHeaderAccName(rHeaderAccName)
+ , m_rHeaderAccType(rHeaderAccType)
+ , m_rHeaderAccValue(rHeaderAccValue)
+{
+ m_aEditLoseFocusIdle.SetPriority( TaskPriority::LOWEST );
+ m_aEditLoseFocusIdle.SetInvokeHandler( LINK( this, CustomPropertiesWindow, EditTimeoutHdl ) );
+ m_aBoxLoseFocusIdle.SetPriority( TaskPriority::LOWEST );
+ m_aBoxLoseFocusIdle.SetInvokeHandler( LINK( this, CustomPropertiesWindow, BoxTimeoutHdl ) );
+}
+
+CustomPropertiesWindow::~CustomPropertiesWindow()
+{
+ m_aEditLoseFocusIdle.Stop();
+ m_aBoxLoseFocusIdle.Stop();
+
+ m_pCurrentLine = nullptr;
+}
+
+void CustomPropertyLine::DoTypeHdl(const weld::ComboBox& rBox)
+{
+ auto nType = rBox.get_active_id().toInt32();
+ m_xValueEdit->set_visible( (Custom_Type_Text == nType) || (Custom_Type_Number == nType) );
+ m_xDateTimeBox->set_visible( (Custom_Type_Date == nType) || (Custom_Type_Datetime == nType) );
+ m_xDateField->set_visible( (Custom_Type_Date == nType) || (Custom_Type_Datetime == nType) );
+ m_xTimeField->set_visible( Custom_Type_Datetime == nType );
+ m_xDurationBox->set_visible( Custom_Type_Duration == nType );
+ m_xDurationField->set_visible( Custom_Type_Duration == nType );
+ m_xYesNoButton->set_visible( Custom_Type_Boolean == nType );
+}
+
+IMPL_LINK(CustomPropertyLine, TypeHdl, weld::ComboBox&, rBox, void)
+{
+ DoTypeHdl(rBox);
+}
+
+void CustomPropertiesWindow::Remove(const CustomPropertyLine* pLine)
+{
+ StoreCustomProperties();
+
+ auto pFound = std::find_if( m_aCustomPropertiesLines.begin(), m_aCustomPropertiesLines.end(),
+ [&] (const std::unique_ptr<CustomPropertyLine>& p) { return p.get() == pLine; });
+ if ( pFound != m_aCustomPropertiesLines.end() )
+ {
+ sal_uInt32 nLineNumber = pFound - m_aCustomPropertiesLines.begin();
+ sal_uInt32 nDataModelIndex = GetCurrentDataModelPosition() + nLineNumber;
+ m_aCustomProperties.erase(m_aCustomProperties.begin() + nDataModelIndex);
+
+ ReloadLinesContent();
+ }
+
+ m_aRemovedHdl.Call(nullptr);
+}
+
+IMPL_LINK_NOARG(CustomPropertyLine, RemoveHdl, weld::Button&, void)
+{
+ m_pParent->Remove(this);
+}
+
+void CustomPropertiesWindow::EditLoseFocus(CustomPropertyLine* pLine)
+{
+ m_pCurrentLine = pLine;
+ m_aEditLoseFocusIdle.Start();
+}
+
+IMPL_LINK_NOARG(CustomPropertyLine, EditLoseFocusHdl, weld::Widget&, void)
+{
+ if (!m_bTypeLostFocus)
+ m_pParent->EditLoseFocus(this);
+ else
+ m_bTypeLostFocus = false;
+}
+
+void CustomPropertiesWindow::BoxLoseFocus(CustomPropertyLine* pLine)
+{
+ m_pCurrentLine = pLine;
+ m_aBoxLoseFocusIdle.Start();
+}
+
+IMPL_LINK_NOARG(CustomPropertyLine, BoxLoseFocusHdl, weld::Widget&, void)
+{
+ m_pParent->BoxLoseFocus(this);
+}
+
+IMPL_LINK_NOARG(CustomPropertiesWindow, EditTimeoutHdl, Timer *, void)
+{
+ ValidateLine( m_pCurrentLine, false );
+}
+
+IMPL_LINK_NOARG(CustomPropertiesWindow, BoxTimeoutHdl, Timer *, void)
+{
+ ValidateLine( m_pCurrentLine, true );
+}
+
+bool CustomPropertiesWindow::IsLineValid( CustomPropertyLine* pLine ) const
+{
+ bool bIsValid = true;
+ pLine->m_bTypeLostFocus = false;
+ auto nType = pLine->m_xTypeBox->get_active_id().toInt32();
+ OUString sValue = pLine->m_xValueEdit->get_text();
+ if ( sValue.isEmpty() )
+ return true;
+
+ sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ if ( Custom_Type_Number == nType )
+ // tdf#116214 Scientific format allows to use also standard numbers
+ nIndex = const_cast< SvNumberFormatter& >(
+ m_aNumberFormatter ).GetFormatIndex( NF_SCIENTIFIC_000E00 );
+ else if ( Custom_Type_Date == nType )
+ nIndex = const_cast< SvNumberFormatter& >(
+ m_aNumberFormatter).GetFormatIndex( NF_DATE_SYS_DDMMYYYY );
+
+ if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ sal_uInt32 nTemp = nIndex;
+ double fDummy = 0.0;
+ bIsValid = const_cast< SvNumberFormatter& >(
+ m_aNumberFormatter ).IsNumberFormat( sValue, nIndex, fDummy );
+ if ( bIsValid && nTemp != nIndex )
+ // sValue is a number but the format doesn't match the index
+ bIsValid = false;
+ }
+
+ return bIsValid;
+}
+
+void CustomPropertiesWindow::ValidateLine( CustomPropertyLine* pLine, bool bIsFromTypeBox )
+{
+ if (pLine && !IsLineValid(pLine))
+ {
+ if ( bIsFromTypeBox ) // LoseFocus of TypeBox
+ pLine->m_bTypeLostFocus = true;
+ std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(&m_rBody,
+ VclMessageType::Question, VclButtonsType::OkCancel, SfxResId(STR_SFX_QUERY_WRONG_TYPE)));
+ if (xMessageBox->run() == RET_OK)
+ pLine->m_xTypeBox->set_active_id(OUString::number(Custom_Type_Text));
+ else
+ pLine->m_xValueEdit->grab_focus();
+ }
+}
+
+void CustomPropertiesWindow::SetVisibleLineCount(sal_uInt32 nCount)
+{
+ while (GetExistingLineCount() < nCount)
+ {
+ CreateNewLine();
+ }
+}
+
+void CustomPropertiesWindow::AddLine(const OUString& sName, Any const & rAny)
+{
+ m_aCustomProperties.push_back(std::unique_ptr<CustomProperty>(new CustomProperty(sName, rAny)));
+ ReloadLinesContent();
+}
+
+void CustomPropertiesWindow::CreateNewLine()
+{
+ CustomPropertyLine* pNewLine = new CustomPropertyLine(this, &m_rBody);
+ pNewLine->m_xNameBox->set_accessible_relation_labeled_by(&m_rHeaderAccName);
+ pNewLine->m_xNameBox->set_accessible_name(m_rHeaderAccName.get_label());
+ pNewLine->m_xTypeBox->set_accessible_relation_labeled_by(&m_rHeaderAccType);
+ pNewLine->m_xTypeBox->set_accessible_name(m_rHeaderAccType.get_label());
+ pNewLine->m_xValueEdit->set_accessible_relation_labeled_by(&m_rHeaderAccValue);
+ pNewLine->m_xValueEdit->set_accessible_name(m_rHeaderAccValue.get_label());
+
+ m_aCustomPropertiesLines.emplace_back( pNewLine );
+
+ // for ui-testing. Distinguish the elements in the lines
+ sal_uInt16 nSize = m_aCustomPropertiesLines.size();
+ pNewLine->m_xNameBox->set_buildable_name(
+ pNewLine->m_xNameBox->get_buildable_name() + OString::number(nSize));
+ pNewLine->m_xTypeBox->set_buildable_name(
+ pNewLine->m_xTypeBox->get_buildable_name() + OString::number(nSize));
+ pNewLine->m_xValueEdit->set_buildable_name(
+ pNewLine->m_xValueEdit->get_buildable_name() + OString::number(nSize));
+ pNewLine->m_xRemoveButton->set_buildable_name(
+ pNewLine->m_xRemoveButton->get_buildable_name() + OString::number(nSize));
+
+ pNewLine->DoTypeHdl(*pNewLine->m_xTypeBox);
+}
+
+bool CustomPropertiesWindow::AreAllLinesValid() const
+{
+ bool bRet = true;
+ for ( std::unique_ptr<CustomPropertyLine> const & pLine : m_aCustomPropertiesLines )
+ {
+ if ( !IsLineValid( pLine.get() ) )
+ {
+ bRet = false;
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+void CustomPropertiesWindow::ClearAllLines()
+{
+ for (auto& pLine : m_aCustomPropertiesLines)
+ {
+ pLine->Clear();
+ }
+ m_pCurrentLine = nullptr;
+ m_aCustomProperties.clear();
+ m_nScrollPos = 0;
+}
+
+void CustomPropertiesWindow::DoScroll( sal_Int32 nNewPos )
+{
+ StoreCustomProperties();
+ m_nScrollPos += nNewPos;
+ ReloadLinesContent();
+}
+
+Sequence< beans::PropertyValue > CustomPropertiesWindow::GetCustomProperties()
+{
+ StoreCustomProperties();
+
+ Sequence< beans::PropertyValue > aPropertiesSeq(GetTotalLineCount());
+ std::transform(
+ m_aCustomProperties.begin(), m_aCustomProperties.end(), aPropertiesSeq.getArray(),
+ [](const auto& el) { return comphelper::makePropertyValue(el->m_sName, el->m_aValue); });
+
+ return aPropertiesSeq;
+}
+
+CustomPropertiesTimeField::CustomPropertiesTimeField(std::unique_ptr<weld::FormattedSpinButton> xTimeField)
+ : m_xTimeField(std::move(xTimeField))
+ , m_xFormatter(new weld::TimeFormatter(*m_xTimeField))
+ , m_isUTC(false)
+{
+ m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration);
+ m_xFormatter->EnableEmptyField(false);
+}
+
+tools::Time CustomPropertiesTimeField::get_value() const
+{
+ return m_xFormatter->GetTime();
+}
+
+void CustomPropertiesTimeField::set_value(const tools::Time& rTime)
+{
+ m_xFormatter->SetTime(rTime);
+}
+
+CustomPropertiesTimeField::~CustomPropertiesTimeField()
+{
+}
+
+CustomPropertiesDateField::CustomPropertiesDateField(SvtCalendarBox* pDateField)
+ : m_xDateField(pDateField)
+{
+ DateTime aDateTime(DateTime::SYSTEM);
+ m_xDateField->set_date(aDateTime);
+}
+
+void CustomPropertiesDateField::set_visible(bool bVisible)
+{
+ m_xDateField->set_visible(bVisible);
+}
+
+Date CustomPropertiesDateField::get_date() const
+{
+ return m_xDateField->get_date();
+}
+
+void CustomPropertiesDateField::set_date(const Date& rDate)
+{
+ m_xDateField->set_date(rDate);
+}
+
+CustomPropertiesDateField::~CustomPropertiesDateField()
+{
+}
+
+void CustomPropertiesWindow::StoreCustomProperties()
+{
+ sal_uInt32 nDataModelPos = GetCurrentDataModelPosition();
+
+ for (sal_uInt32 i = 0; nDataModelPos + i < GetTotalLineCount() && i < GetExistingLineCount(); i++)
+ {
+ CustomPropertyLine* pLine = m_aCustomPropertiesLines[i].get();
+
+ OUString sPropertyName = pLine->m_xNameBox->get_active_text();
+ if (!sPropertyName.isEmpty())
+ {
+ m_aCustomProperties[nDataModelPos + i]->m_sName = sPropertyName;
+ auto nType = pLine->m_xTypeBox->get_active_id().toInt32();
+ if (Custom_Type_Number == nType)
+ {
+ double nValue = 0;
+ sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex(NF_NUMBER_SYSTEM);
+ bool bIsNum = m_aNumberFormatter.
+ IsNumberFormat(pLine->m_xValueEdit->get_text(), nIndex, nValue);
+ if (bIsNum)
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= nValue;
+ }
+ else if (Custom_Type_Boolean == nType)
+ {
+ bool bValue = pLine->m_xYesNoButton->IsYesChecked();
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= bValue;
+ }
+ else if (Custom_Type_Datetime == nType)
+ {
+ Date aTmpDate = pLine->m_xDateField->get_date();
+ tools::Time aTmpTime = pLine->m_xTimeField->get_value();
+ util::DateTime const aDateTime(aTmpTime.GetNanoSec(),
+ aTmpTime.GetSec(), aTmpTime.GetMin(), aTmpTime.GetHour(),
+ aTmpDate.GetDay(), aTmpDate.GetMonth(), aTmpDate.GetYear(),
+ pLine->m_xTimeField->m_isUTC);
+ if (pLine->m_xDateField->m_TZ)
+ {
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= util::DateTimeWithTimezone(
+ aDateTime, *pLine->m_xDateField->m_TZ);
+ }
+ else
+ {
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= aDateTime;
+ }
+ }
+ else if (Custom_Type_Date == nType)
+ {
+ Date aTmpDate = pLine->m_xDateField->get_date();
+ util::Date const aDate(aTmpDate.GetDay(), aTmpDate.GetMonth(),
+ aTmpDate.GetYear());
+ if (pLine->m_xDateField->m_TZ)
+ {
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= util::DateWithTimezone(
+ aDate, *pLine->m_xDateField->m_TZ);
+ }
+ else
+ {
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= aDate;
+ }
+ }
+ else if (Custom_Type_Duration == nType)
+ {
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= pLine->m_xDurationField->GetDuration();
+ }
+ else
+ {
+ OUString sValue(pLine->m_xValueEdit->get_text());
+ m_aCustomProperties[nDataModelPos + i]->m_aValue <<= sValue;
+ }
+ }
+ }
+}
+
+void CustomPropertiesWindow::SetCustomProperties(std::vector< std::unique_ptr<CustomProperty> >&& rProperties)
+{
+ m_aCustomProperties = std::move(rProperties);
+ ReloadLinesContent();
+}
+
+void CustomPropertiesWindow::ReloadLinesContent()
+{
+ double nTmpValue = 0;
+ bool bTmpValue = false;
+ OUString sTmpValue;
+ util::DateTime aTmpDateTime;
+ util::Date aTmpDate;
+ util::DateTimeWithTimezone aTmpDateTimeTZ;
+ util::DateWithTimezone aTmpDateTZ;
+ util::Duration aTmpDuration;
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleWrapper = aSysLocale.GetLocaleData();
+ CustomProperties nType = Custom_Type_Unknown;
+ OUString sValue;
+
+ sal_uInt32 nDataModelPos = GetCurrentDataModelPosition();
+ sal_uInt32 i = 0;
+
+ for (; nDataModelPos + i < GetTotalLineCount() && i < GetExistingLineCount(); i++)
+ {
+ const OUString& rName = m_aCustomProperties[nDataModelPos + i]->m_sName;
+ const css::uno::Any& rAny = m_aCustomProperties[nDataModelPos + i]->m_aValue;
+
+ CustomPropertyLine* pLine = m_aCustomPropertiesLines[i].get();
+ pLine->Clear();
+
+ pLine->m_xNameBox->set_entry_text(rName);
+ pLine->m_xLine->show();
+
+ if (!rAny.hasValue())
+ {
+ pLine->m_xValueEdit->set_text(OUString());
+ }
+ else if (rAny >>= nTmpValue)
+ {
+ sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex(NF_NUMBER_SYSTEM);
+ m_aNumberFormatter.GetInputLineString(nTmpValue, nIndex, sValue);
+ pLine->m_xValueEdit->set_text(sValue);
+ nType = Custom_Type_Number;
+ }
+ else if (rAny >>= bTmpValue)
+ {
+ sValue = (bTmpValue ? rLocaleWrapper.getTrueWord() : rLocaleWrapper.getFalseWord());
+ nType = Custom_Type_Boolean;
+ }
+ else if (rAny >>= sTmpValue)
+ {
+ pLine->m_xValueEdit->set_text(sTmpValue);
+ nType = Custom_Type_Text;
+ }
+ else if (rAny >>= aTmpDate)
+ {
+ pLine->m_xDateField->set_date(Date(aTmpDate));
+ nType = Custom_Type_Date;
+ }
+ else if (rAny >>= aTmpDateTime)
+ {
+ pLine->m_xDateField->set_date(Date(aTmpDateTime));
+ pLine->m_xTimeField->set_value(tools::Time(aTmpDateTime));
+ pLine->m_xTimeField->m_isUTC = aTmpDateTime.IsUTC;
+ nType = Custom_Type_Datetime;
+ }
+ else if (rAny >>= aTmpDateTZ)
+ {
+ pLine->m_xDateField->set_date(Date(aTmpDateTZ.DateInTZ.Day,
+ aTmpDateTZ.DateInTZ.Month, aTmpDateTZ.DateInTZ.Year));
+ pLine->m_xDateField->m_TZ = aTmpDateTZ.Timezone;
+ nType = Custom_Type_Date;
+ }
+
+ else if (rAny >>= aTmpDateTimeTZ)
+ {
+ util::DateTime const& rDT(aTmpDateTimeTZ.DateTimeInTZ);
+ pLine->m_xDateField->set_date(Date(rDT));
+ pLine->m_xTimeField->set_value(tools::Time(rDT));
+ pLine->m_xTimeField->m_isUTC = rDT.IsUTC;
+ pLine->m_xDateField->m_TZ = aTmpDateTimeTZ.Timezone;
+ nType = Custom_Type_Datetime;
+ }
+ else if (rAny >>= aTmpDuration)
+ {
+ nType = Custom_Type_Duration;
+ pLine->m_xDurationField->SetDuration(aTmpDuration);
+ }
+
+ if (nType != Custom_Type_Duration)
+ {
+ if (Custom_Type_Boolean == nType)
+ {
+ if (bTmpValue)
+ pLine->m_xYesNoButton->CheckYes();
+ else
+ pLine->m_xYesNoButton->CheckNo();
+ }
+ pLine->m_xTypeBox->set_active_id(OUString::number(nType));
+ }
+
+ pLine->DoTypeHdl(*pLine->m_xTypeBox);
+ }
+
+ // tdf#132667 - grab focus on the last inserted property
+ if (i > 0 && m_aCustomProperties[nDataModelPos + i - 1]->m_sName.isEmpty())
+ {
+ CustomPropertyLine* pLine = m_aCustomPropertiesLines[i - 1].get();
+ pLine->m_xNameBox->grab_focus();
+ }
+
+ while (nDataModelPos + i >= GetTotalLineCount() && i < GetExistingLineCount())
+ {
+ CustomPropertyLine* pLine = m_aCustomPropertiesLines[i].get();
+ pLine->Hide();
+ i++;
+ }
+}
+
+CustomPropertiesControl::CustomPropertiesControl()
+ : m_nThumbPos(0)
+{
+}
+
+void CustomPropertiesControl::Init(weld::Builder& rBuilder)
+{
+ m_xBox = rBuilder.weld_widget("box");
+ m_xBody = rBuilder.weld_container("properties");
+
+ m_xName = rBuilder.weld_label("name");
+ m_xType = rBuilder.weld_label("type");
+ m_xValue = rBuilder.weld_label("value");
+ m_xVertScroll = rBuilder.weld_scrolled_window("scroll", true);
+ m_xPropertiesWin.reset(new CustomPropertiesWindow(*m_xBody, *m_xName, *m_xType, *m_xValue));
+
+ m_xBox->set_stack_background();
+ m_xVertScroll->show();
+
+ std::unique_ptr<CustomPropertyLine> xNewLine(new CustomPropertyLine(m_xPropertiesWin.get(), m_xBody.get()));
+ Size aLineSize(xNewLine->m_xLine->get_preferred_size());
+ m_xPropertiesWin->SetLineHeight(aLineSize.Height() + 6);
+ m_xBody->set_size_request(aLineSize.Width() + 6, -1);
+ auto nHeight = aLineSize.Height() * 8;
+ m_xVertScroll->set_size_request(-1, nHeight + 6);
+
+ m_xPropertiesWin->SetHeight(nHeight);
+ m_xVertScroll->connect_size_allocate(LINK(this, CustomPropertiesControl, ResizeHdl));
+
+ m_xName->set_size_request(xNewLine->m_xNameBox->get_preferred_size().Width(), -1);
+ m_xType->set_size_request(xNewLine->m_xTypeBox->get_preferred_size().Width(), -1);
+ m_xValue->set_size_request(xNewLine->m_xValueEdit->get_preferred_size().Width(), -1);
+
+ m_xBody->move(xNewLine->m_xLine.get(), nullptr);
+ xNewLine.reset();
+
+ m_xPropertiesWin->SetRemovedHdl( LINK( this, CustomPropertiesControl, RemovedHdl ) );
+
+ m_xVertScroll->vadjustment_set_lower(0);
+ m_xVertScroll->vadjustment_set_upper(0);
+ m_xVertScroll->vadjustment_set_page_size(0xFFFF);
+
+ Link<weld::ScrolledWindow&,void> aScrollLink = LINK( this, CustomPropertiesControl, ScrollHdl );
+ m_xVertScroll->connect_vadjustment_changed(aScrollLink);
+
+ ResizeHdl(Size(-1, nHeight));
+}
+
+IMPL_LINK(CustomPropertiesControl, ResizeHdl, const Size&, rSize, void)
+{
+ int nHeight = rSize.Height() - 6;
+ if (nHeight == m_xPropertiesWin->GetHeight())
+ return;
+ m_xPropertiesWin->SetHeight(nHeight);
+ sal_Int32 nScrollOffset = m_xPropertiesWin->GetLineHeight();
+ sal_Int32 nVisibleEntries = nHeight / nScrollOffset;
+ m_xPropertiesWin->SetVisibleLineCount( nVisibleEntries );
+ m_xVertScroll->vadjustment_set_page_increment( nVisibleEntries - 1 );
+ m_xVertScroll->vadjustment_set_page_size( nVisibleEntries );
+ m_xPropertiesWin->ReloadLinesContent();
+}
+
+CustomPropertiesControl::~CustomPropertiesControl()
+{
+}
+
+IMPL_LINK( CustomPropertiesControl, ScrollHdl, weld::ScrolledWindow&, rScrollBar, void )
+{
+ sal_Int32 nOffset = m_xPropertiesWin->GetLineHeight();
+ int nThumbPos = rScrollBar.vadjustment_get_value();
+ nOffset *= ( m_nThumbPos - nThumbPos );
+ m_nThumbPos = nThumbPos;
+ m_xPropertiesWin->DoScroll( nOffset );
+}
+
+IMPL_LINK_NOARG(CustomPropertiesControl, RemovedHdl, void*, void)
+{
+ auto nLineCount = m_xPropertiesWin->GetTotalLineCount();
+ m_xVertScroll->vadjustment_set_upper(nLineCount + 1);
+ if (m_xPropertiesWin->GetTotalLineCount() > m_xPropertiesWin->GetExistingLineCount())
+ {
+ m_xVertScroll->vadjustment_set_value(nLineCount - 1);
+ ScrollHdl(*m_xVertScroll);
+ }
+}
+
+void CustomPropertiesControl::AddLine( Any const & rAny )
+{
+ m_xPropertiesWin->AddLine( OUString(), rAny );
+ auto nLineCount = m_xPropertiesWin->GetTotalLineCount();
+ m_xVertScroll->vadjustment_set_upper(nLineCount + 1);
+ if (m_xPropertiesWin->GetHeight() < nLineCount * m_xPropertiesWin->GetLineHeight())
+ {
+ m_xVertScroll->vadjustment_set_value(nLineCount + 1);
+ ScrollHdl(*m_xVertScroll);
+ }
+}
+
+void CustomPropertiesControl::SetCustomProperties(std::vector< std::unique_ptr<CustomProperty> >&& rProperties)
+{
+ m_xPropertiesWin->SetCustomProperties(std::move(rProperties));
+ auto nLineCount = m_xPropertiesWin->GetTotalLineCount();
+ m_xVertScroll->vadjustment_set_upper(nLineCount + 1);
+}
+
+// class SfxCustomPropertiesPage -----------------------------------------
+SfxCustomPropertiesPage::SfxCustomPropertiesPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet )
+ : SfxTabPage(pPage, pController, "sfx/ui/custominfopage.ui", "CustomInfoPage", &rItemSet)
+ , m_xPropertiesCtrl(new CustomPropertiesControl)
+ , m_xAdd(m_xBuilder->weld_button("add"))
+{
+ m_xPropertiesCtrl->Init(*m_xBuilder);
+ m_xAdd->connect_clicked(LINK(this, SfxCustomPropertiesPage, AddHdl));
+}
+
+SfxCustomPropertiesPage::~SfxCustomPropertiesPage()
+{
+ m_xPropertiesCtrl.reset();
+}
+
+IMPL_LINK_NOARG(SfxCustomPropertiesPage, AddHdl, weld::Button&, void)
+{
+ // tdf#115853: reload current lines before adding a brand new one
+ // indeed the info are deleted by ClearCustomProperties
+ // each time SfxDocumentInfoItem destructor is called
+ SfxDocumentInfoItem pInfo;
+ const Sequence< beans::PropertyValue > aPropertySeq = m_xPropertiesCtrl->GetCustomProperties();
+ for ( const auto& rProperty : aPropertySeq )
+ {
+ if ( !rProperty.Name.isEmpty() )
+ {
+ pInfo.AddCustomProperty( rProperty.Name, rProperty.Value );
+ }
+ }
+
+ Any aAny;
+ m_xPropertiesCtrl->AddLine(aAny);
+}
+
+bool SfxCustomPropertiesPage::FillItemSet( SfxItemSet* rSet )
+{
+ const SfxDocumentInfoItem* pItem = nullptr;
+ SfxDocumentInfoItem* pInfo = nullptr;
+ bool bMustDelete = false;
+
+ if (const SfxItemSet* pItemSet = GetDialogExampleSet())
+ {
+ pItem = pItemSet->GetItemIfSet(SID_DOCINFO);
+ if (!pItem)
+ pInfo = const_cast<SfxDocumentInfoItem*>(&rSet->Get( SID_DOCINFO ));
+ else
+ {
+ bMustDelete = true;
+ pInfo = new SfxDocumentInfoItem( *pItem );
+ }
+ }
+
+ if ( pInfo )
+ {
+ // If it's a CMIS document, we can't save custom properties
+ if ( pInfo->isCmisDocument( ) )
+ {
+ if ( bMustDelete )
+ delete pInfo;
+ return false;
+ }
+
+ pInfo->ClearCustomProperties();
+ const Sequence< beans::PropertyValue > aPropertySeq = m_xPropertiesCtrl->GetCustomProperties();
+ for ( const auto& rProperty : aPropertySeq )
+ {
+ if ( !rProperty.Name.isEmpty() )
+ pInfo->AddCustomProperty( rProperty.Name, rProperty.Value );
+ }
+ }
+
+ if (pInfo)
+ {
+ rSet->Put(*pInfo);
+ if ( bMustDelete )
+ delete pInfo;
+ }
+ return true;
+}
+
+void SfxCustomPropertiesPage::Reset( const SfxItemSet* rItemSet )
+{
+ m_xPropertiesCtrl->ClearAllLines();
+ const SfxDocumentInfoItem& rInfoItem = rItemSet->Get(SID_DOCINFO);
+ std::vector< std::unique_ptr<CustomProperty> > aCustomProps = rInfoItem.GetCustomProperties();
+ // tdf#123919 - sort custom document properties
+ auto const sort = comphelper::string::NaturalStringSorter(
+ comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetLanguageTag().getLocale());
+ std::sort(aCustomProps.begin(), aCustomProps.end(),
+ [&sort](const std::unique_ptr<CustomProperty>& rLHS,
+ const std::unique_ptr<CustomProperty>& rRHS) {
+ return sort.compare(rLHS->m_sName, rRHS->m_sName) < 0;
+ });
+ m_xPropertiesCtrl->SetCustomProperties(std::move(aCustomProps));
+}
+
+DeactivateRC SfxCustomPropertiesPage::DeactivatePage( SfxItemSet* /*pSet*/ )
+{
+ DeactivateRC nRet = DeactivateRC::LeavePage;
+ if ( !m_xPropertiesCtrl->AreAllLinesValid() )
+ nRet = DeactivateRC::KeepPage;
+ return nRet;
+}
+
+std::unique_ptr<SfxTabPage> SfxCustomPropertiesPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rItemSet)
+{
+ return std::make_unique<SfxCustomPropertiesPage>(pPage, pController, *rItemSet);
+}
+
+CmisValue::CmisValue(weld::Widget* pParent, const OUString& aStr)
+ : m_xBuilder(Application::CreateBuilder(pParent, "sfx/ui/cmisline.ui"))
+ , m_xFrame(m_xBuilder->weld_frame("CmisFrame"))
+ , m_xValueEdit(m_xBuilder->weld_entry("value"))
+{
+ m_xValueEdit->show();
+ m_xValueEdit->set_text(aStr);
+}
+
+CmisDateTime::CmisDateTime(weld::Widget* pParent, const util::DateTime& aDateTime)
+ : m_xBuilder(Application::CreateBuilder(pParent, "sfx/ui/cmisline.ui"))
+ , m_xFrame(m_xBuilder->weld_frame("CmisFrame"))
+ , m_xDateField(new SvtCalendarBox(m_xBuilder->weld_menu_button("date")))
+ , m_xTimeField(m_xBuilder->weld_formatted_spin_button("time"))
+ , m_xFormatter(new weld::TimeFormatter(*m_xTimeField))
+{
+ m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration);
+ m_xFormatter->EnableEmptyField(false);
+
+ m_xDateField->show();
+ m_xTimeField->show();
+ m_xDateField->set_date(Date(aDateTime));
+ m_xFormatter->SetTime(tools::Time(aDateTime));
+}
+
+CmisYesNo::CmisYesNo(weld::Widget* pParent, bool bValue)
+ : m_xBuilder(Application::CreateBuilder(pParent, "sfx/ui/cmisline.ui"))
+ , m_xFrame(m_xBuilder->weld_frame("CmisFrame"))
+ , m_xYesButton(m_xBuilder->weld_radio_button("yes"))
+ , m_xNoButton(m_xBuilder->weld_radio_button("no"))
+{
+ m_xYesButton->show();
+ m_xNoButton->show();
+ if (bValue)
+ m_xYesButton->set_active(true);
+ else
+ m_xNoButton->set_active(true);
+}
+
+// struct CmisPropertyLine ---------------------------------------------
+CmisPropertyLine::CmisPropertyLine(weld::Widget* pParent)
+ : m_xBuilder(Application::CreateBuilder(pParent, "sfx/ui/cmisline.ui"))
+ , m_sType(CMIS_TYPE_STRING)
+ , m_bUpdatable(false)
+ , m_bRequired(false)
+ , m_bMultiValued(false)
+ , m_bOpenChoice(false)
+ , m_xFrame(m_xBuilder->weld_frame("CmisFrame"))
+ , m_xName(m_xBuilder->weld_label("name"))
+ , m_xType(m_xBuilder->weld_label("type"))
+{
+ m_xFrame->set_sensitive(true);
+}
+
+CmisPropertyLine::~CmisPropertyLine( )
+{
+}
+
+// class CmisPropertiesWindow -----------------------------------------
+
+CmisPropertiesWindow::CmisPropertiesWindow(std::unique_ptr<weld::Container> xParent)
+ : m_xBox(std::move(xParent))
+ , m_aNumberFormatter(::comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetLanguageTag().getLanguageType())
+{
+}
+
+CmisPropertiesWindow::~CmisPropertiesWindow()
+{
+}
+
+void CmisPropertiesWindow::ClearAllLines()
+{
+ m_aCmisPropertiesLines.clear();
+}
+
+void CmisPropertiesWindow::AddLine( const OUString& sId, const OUString& sName,
+ const OUString& sType, const bool bUpdatable,
+ const bool bRequired, const bool bMultiValued,
+ const bool bOpenChoice, Any& /*aChoices*/, Any const & rAny )
+{
+ std::unique_ptr<CmisPropertyLine> pNewLine(new CmisPropertyLine(m_xBox.get()));
+
+ pNewLine->m_sId = sId;
+ pNewLine->m_sType = sType;
+ pNewLine->m_bUpdatable = bUpdatable;
+ pNewLine->m_bRequired = bRequired;
+ pNewLine->m_bMultiValued = bMultiValued;
+ pNewLine->m_bOpenChoice = bOpenChoice;
+
+ if ( sType == CMIS_TYPE_INTEGER )
+ {
+ Sequence< sal_Int64 > seqValue;
+ rAny >>= seqValue;
+ sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex( NF_NUMBER_SYSTEM );
+ for ( const auto& rValue : std::as_const(seqValue) )
+ {
+ OUString sValue;
+ m_aNumberFormatter.GetInputLineString( rValue, nIndex, sValue );
+ std::unique_ptr<CmisValue> pValue(new CmisValue(m_xBox.get(), sValue));
+ pValue->m_xValueEdit->set_editable(bUpdatable);
+ pNewLine->m_aValues.push_back( std::move(pValue) );
+ }
+ }
+ else if ( sType == CMIS_TYPE_DECIMAL )
+ {
+ Sequence< double > seqValue;
+ rAny >>= seqValue;
+ sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex( NF_NUMBER_SYSTEM );
+ for ( const auto& rValue : std::as_const(seqValue) )
+ {
+ OUString sValue;
+ m_aNumberFormatter.GetInputLineString( rValue, nIndex, sValue );
+ std::unique_ptr<CmisValue> pValue(new CmisValue(m_xBox.get(), sValue));
+ pValue->m_xValueEdit->set_editable(bUpdatable);
+ pNewLine->m_aValues.push_back( std::move(pValue) );
+ }
+
+ }
+ else if ( sType == CMIS_TYPE_BOOL )
+ {
+ Sequence<sal_Bool> seqValue;
+ rAny >>= seqValue;
+ for ( const auto& rValue : std::as_const(seqValue) )
+ {
+ std::unique_ptr<CmisYesNo> pYesNo(new CmisYesNo(m_xBox.get(), rValue));
+ pYesNo->m_xYesButton->set_sensitive( bUpdatable );
+ pYesNo->m_xNoButton->set_sensitive( bUpdatable );
+ pNewLine->m_aYesNos.push_back( std::move(pYesNo) );
+ }
+ }
+ else if ( sType == CMIS_TYPE_STRING )
+ {
+ Sequence< OUString > seqValue;
+ rAny >>= seqValue;
+ for ( const auto& rValue : std::as_const(seqValue) )
+ {
+ std::unique_ptr<CmisValue> pValue(new CmisValue(m_xBox.get(), rValue));
+ pValue->m_xValueEdit->set_editable(bUpdatable);
+ pNewLine->m_aValues.push_back( std::move(pValue) );
+ }
+ }
+ else if ( sType == CMIS_TYPE_DATETIME )
+ {
+ Sequence< util::DateTime > seqValue;
+ rAny >>= seqValue;
+ for ( const auto& rValue : std::as_const(seqValue) )
+ {
+ std::unique_ptr<CmisDateTime> pDateTime(new CmisDateTime(m_xBox.get(), rValue));
+ pDateTime->m_xDateField->set_sensitive(bUpdatable);
+ pDateTime->m_xTimeField->set_sensitive(bUpdatable);
+ pNewLine->m_aDateTimes.push_back( std::move(pDateTime) );
+ }
+ }
+ pNewLine->m_xName->set_label( sName );
+ pNewLine->m_xName->show();
+ pNewLine->m_xType->set_label( sType );
+ pNewLine->m_xType->show();
+
+ m_aCmisPropertiesLines.push_back( std::move(pNewLine) );
+}
+
+Sequence< document::CmisProperty > CmisPropertiesWindow::GetCmisProperties() const
+{
+ Sequence< document::CmisProperty > aPropertiesSeq( m_aCmisPropertiesLines.size() );
+ auto aPropertiesSeqRange = asNonConstRange(aPropertiesSeq);
+ sal_Int32 i = 0;
+ for ( auto& rxLine : m_aCmisPropertiesLines )
+ {
+ CmisPropertyLine* pLine = rxLine.get();
+
+ aPropertiesSeqRange[i].Id = pLine->m_sId;
+ aPropertiesSeqRange[i].Type = pLine->m_sType;
+ aPropertiesSeqRange[i].Updatable = pLine->m_bUpdatable;
+ aPropertiesSeqRange[i].Required = pLine->m_bRequired;
+ aPropertiesSeqRange[i].OpenChoice = pLine->m_bOpenChoice;
+ aPropertiesSeqRange[i].MultiValued = pLine->m_bMultiValued;
+
+ OUString sPropertyName = pLine->m_xName->get_label();
+ if ( !sPropertyName.isEmpty() )
+ {
+ aPropertiesSeqRange[i].Name = sPropertyName;
+ OUString sType = pLine->m_xType->get_label();
+ if ( CMIS_TYPE_DECIMAL == sType )
+ {
+ sal_uInt32 nIndex = const_cast< SvNumberFormatter& >(
+ m_aNumberFormatter ).GetFormatIndex( NF_NUMBER_SYSTEM );
+ Sequence< double > seqValue( pLine->m_aValues.size( ) );
+ auto seqValueRange = asNonConstRange(seqValue);
+ sal_Int32 k = 0;
+ for ( const auto& rxValue : pLine->m_aValues )
+ {
+ double dValue = 0.0;
+ OUString sValue( rxValue->m_xValueEdit->get_text() );
+ bool bIsNum = const_cast< SvNumberFormatter& >( m_aNumberFormatter ).
+ IsNumberFormat( sValue, nIndex, dValue );
+ if ( bIsNum )
+ seqValueRange[k] = dValue;
+ ++k;
+ }
+ aPropertiesSeqRange[i].Value <<= seqValue;
+ }
+ else if ( CMIS_TYPE_INTEGER == sType )
+ {
+ sal_uInt32 nIndex = const_cast< SvNumberFormatter& >(
+ m_aNumberFormatter ).GetFormatIndex( NF_NUMBER_SYSTEM );
+ Sequence< sal_Int64 > seqValue( pLine->m_aValues.size( ) );
+ auto seqValueRange = asNonConstRange(seqValue);
+ sal_Int32 k = 0;
+ for ( const auto& rxValue : pLine->m_aValues )
+ {
+ double dValue = 0;
+ OUString sValue( rxValue->m_xValueEdit->get_text() );
+ bool bIsNum = const_cast< SvNumberFormatter& >( m_aNumberFormatter ).
+ IsNumberFormat( sValue, nIndex, dValue );
+ if ( bIsNum )
+ seqValueRange[k] = static_cast<sal_Int64>(dValue);
+ ++k;
+ }
+ aPropertiesSeqRange[i].Value <<= seqValue;
+ }
+ else if ( CMIS_TYPE_BOOL == sType )
+ {
+ Sequence<sal_Bool> seqValue( pLine->m_aYesNos.size( ) );
+ sal_Bool* pseqValue = seqValue.getArray();
+ sal_Int32 k = 0;
+ for ( const auto& rxYesNo : pLine->m_aYesNos )
+ {
+ bool bValue = rxYesNo->m_xYesButton->get_active();
+ pseqValue[k] = bValue;
+ ++k;
+ }
+ aPropertiesSeqRange[i].Value <<= seqValue;
+
+ }
+ else if ( CMIS_TYPE_DATETIME == sType )
+ {
+ Sequence< util::DateTime > seqValue( pLine->m_aDateTimes.size( ) );
+ auto seqValueRange = asNonConstRange(seqValue);
+ sal_Int32 k = 0;
+ for ( const auto& rxDateTime : pLine->m_aDateTimes )
+ {
+ Date aTmpDate = rxDateTime->m_xDateField->get_date();
+ tools::Time aTmpTime = rxDateTime->m_xFormatter->GetTime();
+ util::DateTime aDateTime( aTmpTime.GetNanoSec(), aTmpTime.GetSec(),
+ aTmpTime.GetMin(), aTmpTime.GetHour(),
+ aTmpDate.GetDay(), aTmpDate.GetMonth(),
+ aTmpDate.GetYear(), true );
+ seqValueRange[k] = aDateTime;
+ ++k;
+ }
+ aPropertiesSeqRange[i].Value <<= seqValue;
+ }
+ else
+ {
+ Sequence< OUString > seqValue( pLine->m_aValues.size( ) );
+ auto seqValueRange = asNonConstRange(seqValue);
+ sal_Int32 k = 0;
+ for ( const auto& rxValue : pLine->m_aValues )
+ {
+ OUString sValue( rxValue->m_xValueEdit->get_text() );
+ seqValueRange[k] = sValue;
+ ++k;
+ }
+ aPropertiesSeqRange[i].Value <<= seqValue;
+ }
+ }
+ ++i;
+ }
+
+ return aPropertiesSeq;
+}
+
+CmisPropertiesControl::CmisPropertiesControl(weld::Builder& rBuilder)
+ : m_aPropertiesWin(rBuilder.weld_container("CmisWindow"))
+ , m_xScrolledWindow(rBuilder.weld_scrolled_window("CmisScroll"))
+{
+ // set height to something small and force it to take the size
+ // dictated by the other pages
+ m_xScrolledWindow->set_size_request(-1, 42);
+}
+
+void CmisPropertiesControl::ClearAllLines()
+{
+ m_aPropertiesWin.ClearAllLines();
+}
+
+void CmisPropertiesControl::AddLine( const OUString& sId, const OUString& sName,
+ const OUString& sType, const bool bUpdatable,
+ const bool bRequired, const bool bMultiValued,
+ const bool bOpenChoice, Any& aChoices, Any const & rAny
+ )
+{
+ m_aPropertiesWin.AddLine( sId, sName, sType, bUpdatable, bRequired, bMultiValued,
+ bOpenChoice, aChoices, rAny );
+}
+
+// class SfxCmisPropertiesPage -----------------------------------------
+SfxCmisPropertiesPage::SfxCmisPropertiesPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
+ : SfxTabPage(pPage, pController, "sfx/ui/cmisinfopage.ui", "CmisInfoPage", &rItemSet)
+ , m_xPropertiesCtrl(new CmisPropertiesControl(*m_xBuilder))
+{
+}
+
+SfxCmisPropertiesPage::~SfxCmisPropertiesPage()
+{
+ m_xPropertiesCtrl.reset();
+}
+
+bool SfxCmisPropertiesPage::FillItemSet( SfxItemSet* rSet )
+{
+ const SfxDocumentInfoItem* pItem = nullptr;
+ SfxDocumentInfoItem* pInfo = nullptr;
+ bool bMustDelete = false;
+
+ if (const SfxItemSet* pItemSet = GetDialogExampleSet())
+ {
+ pItem = pItemSet->GetItemIfSet(SID_DOCINFO);
+ if (!pItem)
+ pInfo = const_cast<SfxDocumentInfoItem*>(&rSet->Get( SID_DOCINFO ));
+ else
+ {
+ bMustDelete = true;
+ pInfo = new SfxDocumentInfoItem( *pItem );
+ }
+ }
+
+ sal_Int32 modifiedNum = 0;
+ if ( pInfo )
+ {
+ Sequence< document::CmisProperty > aOldProps = pInfo->GetCmisProperties( );
+ Sequence< document::CmisProperty > aNewProps = m_xPropertiesCtrl->GetCmisProperties();
+
+ std::vector< document::CmisProperty > changedProps;
+ for ( sal_Int32 i = 0; i< aNewProps.getLength( ); ++i )
+ {
+ if ( aOldProps[i].Updatable && !aNewProps[i].Id.isEmpty( ) )
+ {
+ if ( aOldProps[i].Type == CMIS_TYPE_DATETIME )
+ {
+ Sequence< util::DateTime > oldValue;
+ aOldProps[i].Value >>= oldValue;
+ // We only edit hours and minutes
+ // don't compare NanoSeconds and Seconds
+ for ( auto& rDateTime : asNonConstRange(oldValue) )
+ {
+ rDateTime.NanoSeconds = 0;
+ rDateTime.Seconds = 0;
+ }
+ Sequence< util::DateTime > newValue;
+ aNewProps[i].Value >>= newValue;
+ if ( oldValue != newValue )
+ {
+ modifiedNum++;
+ changedProps.push_back( aNewProps[i] );
+ }
+ }
+ else if ( aOldProps[i].Value != aNewProps[i].Value )
+ {
+ modifiedNum++;
+ changedProps.push_back( aNewProps[i] );
+ }
+ }
+ }
+ Sequence< document::CmisProperty> aModifiedProps( comphelper::containerToSequence(changedProps) );
+ pInfo->SetCmisProperties( aModifiedProps );
+ rSet->Put( *pInfo );
+ if ( bMustDelete )
+ delete pInfo;
+ }
+
+ return modifiedNum;
+}
+
+void SfxCmisPropertiesPage::Reset( const SfxItemSet* rItemSet )
+{
+ m_xPropertiesCtrl->ClearAllLines();
+ const SfxDocumentInfoItem& rInfoItem = rItemSet->Get(SID_DOCINFO);
+ uno::Sequence< document::CmisProperty > aCmisProps = rInfoItem.GetCmisProperties();
+ for ( auto& rCmisProp : asNonConstRange(aCmisProps) )
+ {
+ m_xPropertiesCtrl->AddLine(rCmisProp.Id,
+ rCmisProp.Name,
+ rCmisProp.Type,
+ rCmisProp.Updatable,
+ rCmisProp.Required,
+ rCmisProp.MultiValued,
+ rCmisProp.OpenChoice,
+ rCmisProp.Choices,
+ rCmisProp.Value);
+ }
+}
+
+DeactivateRC SfxCmisPropertiesPage::DeactivatePage( SfxItemSet* /*pSet*/ )
+{
+ return DeactivateRC::LeavePage;
+}
+
+std::unique_ptr<SfxTabPage> SfxCmisPropertiesPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rItemSet)
+{
+ return std::make_unique<SfxCmisPropertiesPage>(pPage, pController, *rItemSet);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/dockwin.cxx b/sfx2/source/dialog/dockwin.cxx
new file mode 100644
index 000000000..4329b2d90
--- /dev/null
+++ b/sfx2/source/dialog/dockwin.cxx
@@ -0,0 +1,1543 @@
+/* -*- 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 <svl/eitem.hxx>
+#include <svl/solar.hrc>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+
+#include <sfx2/dockwin.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <workwin.hxx>
+#include <splitwin.hxx>
+#include <sfx2/viewsh.hxx>
+
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/ui/theWindowStateConfiguration.hpp>
+#include <com/sun/star/ui/theWindowContentFactoryManager.hpp>
+
+#define MAX_TOGGLEAREA_WIDTH 20
+#define MAX_TOGGLEAREA_HEIGHT 20
+
+using namespace ::com::sun::star;
+
+// If you want to change the number you also have to:
+// - Add new slot ids to sfxsids.hrc
+// - Add new slots to frmslots.sdi
+// - Add new slot definitions to sfx.sdi
+const int NUM_OF_DOCKINGWINDOWS = 10;
+
+namespace {
+
+class SfxTitleDockingWindow : public SfxDockingWindow
+{
+ VclPtr<vcl::Window> m_pWrappedWindow;
+
+public:
+ SfxTitleDockingWindow(
+ SfxBindings* pBindings ,
+ SfxChildWindow* pChildWin ,
+ vcl::Window* pParent ,
+ WinBits nBits);
+ virtual ~SfxTitleDockingWindow() override;
+ virtual void dispose() override;
+
+ vcl::Window* GetWrappedWindow() const { return m_pWrappedWindow; }
+ void SetWrappedWindow(vcl::Window* const pWindow);
+
+ virtual void StateChanged( StateChangedType nType ) override;
+ virtual void Resize() override;
+ virtual void Resizing( Size& rSize ) override;
+};
+
+ struct WindowState
+ {
+ OUString sTitle;
+ };
+}
+
+static bool lcl_getWindowState( const uno::Reference< container::XNameAccess >& xWindowStateMgr, const OUString& rResourceURL, WindowState& rWindowState )
+{
+ bool bResult = false;
+
+ try
+ {
+ uno::Any a;
+ uno::Sequence< beans::PropertyValue > aWindowState;
+ a = xWindowStateMgr->getByName( rResourceURL );
+ if ( a >>= aWindowState )
+ {
+ for ( const auto& rProp : std::as_const(aWindowState) )
+ {
+ if ( rProp.Name == "UIName" )
+ {
+ rProp.Value >>= rWindowState.sTitle;
+ }
+ }
+ }
+
+ bResult = true;
+ }
+ catch ( container::NoSuchElementException& )
+ {
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+SfxDockingWrapper::SfxDockingWrapper( vcl::Window* pParentWnd ,
+ sal_uInt16 nId ,
+ SfxBindings* pBindings ,
+ SfxChildWinInfo* pInfo )
+ : SfxChildWindow( pParentWnd , nId )
+{
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ VclPtr<SfxTitleDockingWindow> pTitleDockWindow = VclPtr<SfxTitleDockingWindow>::Create( pBindings, this, pParentWnd,
+ WB_STDDOCKWIN | WB_CLIPCHILDREN | WB_SIZEABLE | WB_3DLOOK);
+ SetWindow( pTitleDockWindow );
+
+ // Use factory manager to retrieve XWindow factory. That can be used to instantiate
+ // the real window factory.
+ uno::Reference< lang::XSingleComponentFactory > xFactoryMgr = ui::theWindowContentFactoryManager::get(xContext);
+
+ SfxDispatcher* pDispatcher = pBindings->GetDispatcher();
+ uno::Reference< frame::XFrame > xFrame = pDispatcher->GetFrame()->GetFrame().GetFrameInterface();
+ // create a resource URL from the nId provided by the sfx2
+ OUString aResourceURL = "private:resource/dockingwindow/" + OUString::number(nId);
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Frame", uno::Any(xFrame)},
+ {"ResourceURL", uno::Any(aResourceURL)},
+ }));
+
+ uno::Reference< awt::XWindow > xWindow;
+ try
+ {
+ xWindow.set(
+ xFactoryMgr->createInstanceWithArgumentsAndContext( aArgs, xContext ),
+ uno::UNO_QUERY );
+
+ static uno::WeakReference< frame::XModuleManager2 > s_xModuleManager;
+
+ uno::Reference< frame::XModuleManager2 > xModuleManager( s_xModuleManager );
+ if ( !xModuleManager.is() )
+ {
+ xModuleManager = frame::ModuleManager::create(xContext);
+ s_xModuleManager = xModuleManager;
+ }
+
+ static uno::WeakReference< container::XNameAccess > s_xWindowStateConfiguration;
+
+ uno::Reference< container::XNameAccess > xWindowStateConfiguration( s_xWindowStateConfiguration );
+ if ( !xWindowStateConfiguration.is() )
+ {
+ xWindowStateConfiguration = ui::theWindowStateConfiguration::get( xContext );
+ s_xWindowStateConfiguration = xWindowStateConfiguration;
+ }
+
+ OUString sModuleIdentifier = xModuleManager->identify( xFrame );
+
+ uno::Reference< container::XNameAccess > xModuleWindowState(
+ xWindowStateConfiguration->getByName( sModuleIdentifier ),
+ uno::UNO_QUERY );
+ if ( xModuleWindowState.is() )
+ {
+ WindowState aDockWinState;
+ if ( lcl_getWindowState( xModuleWindowState, aResourceURL, aDockWinState ))
+ pTitleDockWindow->SetText( aDockWinState.sTitle );
+ }
+ }
+ catch ( beans::UnknownPropertyException& )
+ {
+ }
+ catch ( uno::RuntimeException& )
+ {
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ VclPtr<vcl::Window> pContentWindow = VCLUnoHelper::GetWindow(xWindow);
+ if ( pContentWindow )
+ pContentWindow->SetStyle( pContentWindow->GetStyle() | WB_DIALOGCONTROL | WB_CHILDDLGCTRL );
+ pTitleDockWindow->SetWrappedWindow(pContentWindow);
+
+ GetWindow()->SetOutputSizePixel( Size( 270, 240 ) );
+
+ static_cast<SfxDockingWindow*>( GetWindow() )->Initialize( pInfo );
+ SetHideNotDelete( true );
+}
+
+std::unique_ptr<SfxChildWindow> SfxDockingWrapper::CreateImpl(vcl::Window *pParent, sal_uInt16 nId,
+ SfxBindings *pBindings, SfxChildWinInfo* pInfo)
+{
+ return std::make_unique<SfxDockingWrapper>(pParent, nId, pBindings, pInfo);
+}
+
+void SfxDockingWrapper::RegisterChildWindow (bool bVis, SfxModule *pMod, SfxChildWindowFlags nFlags)
+{
+ // pre-register a couple of docking windows
+ for (int i=0; i < NUM_OF_DOCKINGWINDOWS; i++ )
+ {
+ sal_uInt16 nID = sal_uInt16(SID_DOCKWIN_START+i);
+ SfxChildWinFactory aFact( SfxDockingWrapper::CreateImpl, nID, 0xffff );
+ aFact.aInfo.nFlags |= nFlags;
+ aFact.aInfo.bVisible = bVis;
+ SfxChildWindow::RegisterChildWindow(pMod, aFact);
+ }
+}
+
+SfxChildWinInfo SfxDockingWrapper::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ static_cast<SfxDockingWindow*>(GetWindow())->FillInfo( aInfo );
+ return aInfo;
+};
+
+SfxTitleDockingWindow::SfxTitleDockingWindow(SfxBindings* pBind, SfxChildWindow* pChildWin,
+ vcl::Window* pParent, WinBits nBits)
+ : SfxDockingWindow(pBind, pChildWin, pParent, nBits)
+ , m_pWrappedWindow(nullptr)
+{
+}
+
+SfxTitleDockingWindow::~SfxTitleDockingWindow()
+{
+ disposeOnce();
+}
+
+void SfxTitleDockingWindow::dispose()
+{
+ m_pWrappedWindow.disposeAndClear();
+ SfxDockingWindow::dispose();
+}
+
+void SfxTitleDockingWindow::SetWrappedWindow( vcl::Window* const pWindow )
+{
+ m_pWrappedWindow = pWindow;
+ if (m_pWrappedWindow)
+ {
+ m_pWrappedWindow->SetParent(this);
+ m_pWrappedWindow->SetSizePixel( GetOutputSizePixel() );
+ m_pWrappedWindow->Show();
+ }
+}
+
+void SfxTitleDockingWindow::StateChanged( StateChangedType nType )
+{
+ if ( nType == StateChangedType::InitShow )
+ {
+ vcl::Window* pWindow = GetWrappedWindow();
+ if ( pWindow )
+ {
+ pWindow->SetSizePixel( GetOutputSizePixel() );
+ pWindow->Show();
+ }
+ }
+
+ SfxDockingWindow::StateChanged(nType);
+}
+
+void SfxTitleDockingWindow::Resize()
+{
+ SfxDockingWindow::Resize();
+ if (m_pWrappedWindow)
+ m_pWrappedWindow->SetSizePixel( GetOutputSizePixel() );
+}
+
+void SfxTitleDockingWindow::Resizing( Size &rSize )
+{
+ SfxDockingWindow::Resizing( rSize );
+ if (m_pWrappedWindow)
+ m_pWrappedWindow->SetSizePixel( GetOutputSizePixel() );
+}
+
+static bool lcl_checkDockingWindowID( sal_uInt16 nID )
+{
+ return nID >= SID_DOCKWIN_START && nID < o3tl::make_unsigned(SID_DOCKWIN_START+NUM_OF_DOCKINGWINDOWS);
+}
+
+static SfxWorkWindow* lcl_getWorkWindowFromXFrame( const uno::Reference< frame::XFrame >& rFrame )
+{
+ // We need to find the corresponding SfxFrame of our XFrame
+ SfxFrame* pFrame = SfxFrame::GetFirst();
+ SfxFrame* pXFrame = nullptr;
+ while ( pFrame )
+ {
+ uno::Reference< frame::XFrame > xViewShellFrame( pFrame->GetFrameInterface() );
+ if ( xViewShellFrame == rFrame )
+ {
+ pXFrame = pFrame;
+ break;
+ }
+ else
+ pFrame = SfxFrame::GetNext( *pFrame );
+ }
+
+ // If we have a SfxFrame we can retrieve the work window (Sfx layout manager for docking windows)
+ if ( pXFrame )
+ return pXFrame->GetWorkWindow_Impl();
+ else
+ return nullptr;
+}
+
+/** Factory function used by the framework layout manager to "create" a docking window with a special name.
+ The string rDockingWindowName MUST BE a valid ID! The ID is pre-defined by a certain slot range located
+ in sfxsids.hrc (currently SID_DOCKWIN_START = 9800).
+*/
+void SfxDockingWindowFactory( const uno::Reference< frame::XFrame >& rFrame, std::u16string_view rDockingWindowName )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nID = sal_uInt16(o3tl::toInt32(rDockingWindowName));
+
+ // Check the range of the provided ID otherwise nothing will happen
+ if ( !lcl_checkDockingWindowID( nID ))
+ return;
+
+ SfxWorkWindow* pWorkWindow = lcl_getWorkWindowFromXFrame( rFrame );
+ if ( pWorkWindow )
+ {
+ SfxChildWindow* pChildWindow = pWorkWindow->GetChildWindow_Impl(nID);
+ if ( !pChildWindow )
+ {
+ // Register window at the workwindow child window list
+ pWorkWindow->SetChildWindow_Impl( nID, true, false );
+ }
+ }
+}
+
+/** Function used by the framework layout manager to determine the visibility state of a docking window with
+ a special name. The string rDockingWindowName MUST BE a valid ID! The ID is pre-defined by a certain slot
+ range located in sfxsids.hrc (currently SID_DOCKWIN_START = 9800).
+*/
+bool IsDockingWindowVisible( const uno::Reference< frame::XFrame >& rFrame, std::u16string_view rDockingWindowName )
+{
+ SolarMutexGuard aGuard;
+
+ sal_uInt16 nID = sal_uInt16(o3tl::toInt32(rDockingWindowName));
+
+ // Check the range of the provided ID otherwise nothing will happen
+ if ( lcl_checkDockingWindowID( nID ))
+ {
+ SfxWorkWindow* pWorkWindow = lcl_getWorkWindowFromXFrame( rFrame );
+ if ( pWorkWindow )
+ {
+ SfxChildWindow* pChildWindow = pWorkWindow->GetChildWindow_Impl(nID);
+ if ( pChildWindow )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+class SfxDockingWindow_Impl
+{
+friend class SfxDockingWindow;
+
+ SfxChildAlignment eLastAlignment;
+ SfxChildAlignment eDockAlignment;
+ bool bConstructed;
+ Size aMinSize;
+ VclPtr<SfxSplitWindow> pSplitWin;
+ Idle aMoveIdle;
+
+ // The following members are only valid in the time from startDocking to
+ // EndDocking:
+ Size aSplitSize;
+ tools::Long nHorizontalSize;
+ tools::Long nVerticalSize;
+ sal_uInt16 nLine;
+ sal_uInt16 nPos;
+ sal_uInt16 nDockLine;
+ sal_uInt16 nDockPos;
+ bool bNewLine;
+ bool bDockingPrevented;
+ OString aWinState;
+
+ explicit SfxDockingWindow_Impl(SfxDockingWindow *pBase);
+ SfxChildAlignment GetLastAlignment() const
+ { return eLastAlignment; }
+ void SetLastAlignment(SfxChildAlignment eAlign)
+ { eLastAlignment = eAlign; }
+ SfxChildAlignment GetDockAlignment() const
+ { return eDockAlignment; }
+ void SetDockAlignment(SfxChildAlignment eAlign)
+ { eDockAlignment = eAlign; }
+};
+
+SfxDockingWindow_Impl::SfxDockingWindow_Impl(SfxDockingWindow* pBase)
+ :eLastAlignment(SfxChildAlignment::NOALIGNMENT)
+ ,eDockAlignment(SfxChildAlignment::NOALIGNMENT)
+ ,bConstructed(false)
+ ,pSplitWin(nullptr)
+ ,aMoveIdle( "sfx::SfxDockingWindow_Impl aMoveIdle" )
+ ,nHorizontalSize(0)
+ ,nVerticalSize(0)
+ ,nLine(0)
+ ,nPos(0)
+ ,nDockLine(0)
+ ,nDockPos(0)
+ ,bNewLine(false)
+ ,bDockingPrevented(false)
+{
+ aMoveIdle.SetPriority(TaskPriority::RESIZE);
+ aMoveIdle.SetInvokeHandler(LINK(pBase,SfxDockingWindow,TimerHdl));
+}
+
+/* [Description]
+
+ This virtual method of the class FloatingWindow keeps track of changes in
+ FloatingSize. If this method is overridden by a derived class,
+ then the FloatingWindow: Resize() must also be called.
+*/
+void SfxDockingWindow::Resize()
+{
+ ResizableDockingWindow::Resize();
+ Invalidate();
+ if ( !pImpl || !pImpl->bConstructed || !pMgr )
+ return;
+
+ if ( IsFloatingMode() )
+ {
+ // start timer for saving window status information
+ pImpl->aMoveIdle.Start();
+ }
+ else
+ {
+ Size aSize( GetSizePixel() );
+ switch ( pImpl->GetDockAlignment() )
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::FIRSTLEFT:
+ case SfxChildAlignment::LASTLEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::FIRSTRIGHT:
+ case SfxChildAlignment::LASTRIGHT:
+ pImpl->nHorizontalSize = aSize.Width();
+ pImpl->aSplitSize = aSize;
+ break;
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::LOWESTTOP:
+ case SfxChildAlignment::HIGHESTTOP:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ case SfxChildAlignment::LOWESTBOTTOM:
+ pImpl->nVerticalSize = aSize.Height();
+ pImpl->aSplitSize = aSize;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* [Description]
+
+ This virtual method of the class DockingWindow makes it possible to
+ intervene in the switching of the floating mode.
+ If this method is overridden by a derived class,
+ then the SfxDockingWindow::PrepareToggleFloatingMode() must be called
+ afterwards, if not FALSE is returned.
+*/
+bool SfxDockingWindow::PrepareToggleFloatingMode()
+{
+ if (!pImpl || !pImpl->bConstructed)
+ return true;
+
+ if ( (Application::IsInModalMode() && IsFloatingMode()) || !pMgr )
+ return false;
+
+ if ( pImpl->bDockingPrevented )
+ return false;
+
+ if (!IsFloatingMode())
+ {
+ // Test, if FloatingMode is permitted.
+ if ( CheckAlignment(GetAlignment(),SfxChildAlignment::NOALIGNMENT) != SfxChildAlignment::NOALIGNMENT )
+ return false;
+
+ if ( pImpl->pSplitWin )
+ {
+ // The DockingWindow is inside a SplitWindow and will be teared of.
+ pImpl->pSplitWin->RemoveWindow(this/*, sal_False*/);
+ pImpl->pSplitWin = nullptr;
+ }
+ }
+ else if ( pMgr )
+ {
+ pImpl->aWinState = GetFloatingWindow()->GetWindowState();
+
+ // Test if it is allowed to dock,
+ if (CheckAlignment(GetAlignment(),pImpl->GetLastAlignment()) == SfxChildAlignment::NOALIGNMENT)
+ return false;
+
+ // Test, if the Workwindow allows for docking at the moment.
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ if ( !pWorkWin->IsDockingAllowed() || !pWorkWin->IsInternalDockingAllowed() )
+ return false;
+ }
+
+ return true;
+}
+
+/* [Description]
+
+ This virtual method of the DockingWindow class sets the internal data of
+ the SfxDockingWindow and ensures the correct alignment on the parent window.
+ Through PrepareToggleFloatMode and Initialize it is ensured that
+ pImpl-> GetLastAlignment() always delivers an allowed alignment. If this
+ method is overridden by a derived class, then first the
+ SfxDockingWindow::ToggleFloatingMode() must be called.
+*/
+void SfxDockingWindow::ToggleFloatingMode()
+{
+ if ( !pImpl || !pImpl->bConstructed || !pMgr )
+ return; // No Handler call
+
+ // Remember old alignment and then switch.
+ // SV has already switched, but the alignment SfxDockingWindow is still
+ // the old one. What I was before?
+ SfxChildAlignment eLastAlign = GetAlignment();
+
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+
+ if (IsFloatingMode())
+ {
+ SetAlignment(SfxChildAlignment::NOALIGNMENT);
+ if ( !pImpl->aWinState.isEmpty() )
+ GetFloatingWindow()->SetWindowState( pImpl->aWinState );
+ else
+ GetFloatingWindow()->SetOutputSizePixel( GetFloatingSize() );
+ }
+ else
+ {
+ if (pImpl->GetDockAlignment() == eLastAlign)
+ {
+ // If ToggleFloatingMode was called, but the DockAlignment still
+ // is unchanged, then this means that it must have been a toggling
+ // through DClick, so use last alignment
+ SetAlignment (pImpl->GetLastAlignment());
+ }
+ else
+ {
+
+ // Toggling was triggered by dragging
+ pImpl->nLine = pImpl->nDockLine;
+ pImpl->nPos = pImpl->nDockPos;
+ SetAlignment (pImpl->GetDockAlignment());
+ }
+
+ // The DockingWindow is now in a SplitWindow
+ pImpl->pSplitWin = pWorkWin->GetSplitWindow_Impl(GetAlignment());
+
+ // The LastAlignment is still the last docked
+ SfxSplitWindow *pSplit = pWorkWin->GetSplitWindow_Impl(pImpl->GetLastAlignment());
+
+ DBG_ASSERT( pSplit, "LastAlignment is not correct!" );
+ if ( pSplit && pSplit != pImpl->pSplitWin )
+ pSplit->ReleaseWindow_Impl(this);
+ if ( pImpl->GetDockAlignment() == eLastAlign )
+ pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize );
+ else
+ pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize, pImpl->nLine, pImpl->nPos, pImpl->bNewLine );
+ if ( !pImpl->pSplitWin->IsFadeIn() )
+ pImpl->pSplitWin->FadeIn();
+ }
+
+ // Keep the old alignment for the next toggle; set it only now due to the
+ // unregister SplitWindow!
+ pImpl->SetLastAlignment(eLastAlign);
+
+ // Reset DockAlignment, if EndDocking is still called
+ pImpl->SetDockAlignment(GetAlignment());
+
+ // Dock or undock SfxChildWindow correctly.
+ pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::TOGGLEFLOATMODE, pMgr->GetType() );
+}
+
+/* [Description]
+
+ This virtual method of the DockingWindow class takes the inner and outer
+ docking rectangle from the parent window. If this method is overridden by
+ a derived class, then SfxDockingWindow:StartDocking() has to be called at
+ the end.
+*/
+void SfxDockingWindow::StartDocking()
+{
+ if ( !pImpl || !pImpl->bConstructed || !pMgr )
+ return;
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::SETDOCKINGRECTS, pMgr->GetType() );
+ pImpl->SetDockAlignment(GetAlignment());
+
+ if ( pImpl->pSplitWin )
+ {
+ // Get the current docking data
+ pImpl->pSplitWin->GetWindowPos(this, pImpl->nLine, pImpl->nPos);
+ pImpl->nDockLine = pImpl->nLine;
+ pImpl->nDockPos = pImpl->nPos;
+ pImpl->bNewLine = false;
+ }
+}
+
+/* [Description]
+
+ This virtual method of the DockingWindow class calculates the current
+ tracking rectangle. For this purpose the method CalcAlignment(RPOs, rRect)
+ is used, the behavior can be influenced by the derived classes (see below).
+ This method should if possible not be overwritten.
+*/
+bool SfxDockingWindow::Docking( const Point& rPos, tools::Rectangle& rRect )
+{
+ if ( Application::IsInModalMode() )
+ return true;
+
+ if ( !pImpl || !pImpl->bConstructed || !pMgr )
+ {
+ rRect.SetSize( Size() );
+ return IsFloatingMode();
+ }
+
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ if ( pImpl->bDockingPrevented || !pWorkWin->IsInternalDockingAllowed() )
+ return false;
+
+ bool bFloatMode = false;
+
+ if ( GetOuterRect().Contains( rPos ) )
+ {
+ // Mouse within OuterRect: calculate Alignment and Rectangle
+ SfxChildAlignment eAlign = CalcAlignment(rPos, rRect);
+ if (eAlign == SfxChildAlignment::NOALIGNMENT)
+ bFloatMode = true;
+ pImpl->SetDockAlignment(eAlign);
+ }
+ else
+ {
+ // Mouse is not within OuterRect: must be FloatingWindow
+ // Is this allowed?
+ if (CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::NOALIGNMENT) != SfxChildAlignment::NOALIGNMENT)
+ return false;
+ bFloatMode = true;
+ if ( SfxChildAlignment::NOALIGNMENT != pImpl->GetDockAlignment() )
+ {
+ // Due to a bug the rRect may only be changed when the
+ // alignment is changed!
+ pImpl->SetDockAlignment(SfxChildAlignment::NOALIGNMENT);
+ rRect.SetSize(CalcDockingSize(SfxChildAlignment::NOALIGNMENT));
+ }
+ }
+
+ return bFloatMode;
+}
+
+/** Virtual method of the DockingWindow class ensures the correct alignment on
+ the parent window. If this method is overridden by a derived class, then
+ SfxDockingWindow::EndDocking() must be called first.
+*/
+void SfxDockingWindow::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
+{
+ if ( !pImpl || !pImpl->bConstructed || IsDockingCanceled() || !pMgr )
+ return;
+
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+
+ // If the alignment changes and the window is in a docked state in a
+ // SplitWindow, then it must be re-registered. If it is docked again,
+ // PrepareToggleFloatingMode() and ToggleFloatingMode() perform the
+ // re-registered
+ bool bReArrange = !bFloatMode;
+
+ if ( bReArrange )
+ {
+ if ( GetAlignment() != pImpl->GetDockAlignment() )
+ {
+ // before Show() is called must the reassignment have been made,
+ // therefore the base class can not be called
+ if ( IsFloatingMode() )
+ Show( false, ShowFlags::NoFocusChange );
+
+ // Set the size for toggling.
+ pImpl->aSplitSize = rRect.GetSize();
+ if ( IsFloatingMode() )
+ {
+ SetFloatingMode( bFloatMode );
+ if ( IsFloatingMode() )
+ Show( true, ShowFlags::NoFocusChange );
+ }
+ else
+ {
+ pImpl->pSplitWin->RemoveWindow(this,false);
+ pImpl->nLine = pImpl->nDockLine;
+ pImpl->nPos = pImpl->nDockPos;
+ pImpl->pSplitWin->ReleaseWindow_Impl(this);
+ pImpl->pSplitWin = pWorkWin->GetSplitWindow_Impl(pImpl->GetDockAlignment());
+ pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize, pImpl->nDockLine, pImpl->nDockPos, pImpl->bNewLine );
+ if ( !pImpl->pSplitWin->IsFadeIn() )
+ pImpl->pSplitWin->FadeIn();
+ }
+ }
+ else if ( pImpl->nLine != pImpl->nDockLine || pImpl->nPos != pImpl->nDockPos || pImpl->bNewLine )
+ {
+ // Moved within Splitwindows
+ if ( pImpl->nLine != pImpl->nDockLine )
+ pImpl->aSplitSize = rRect.GetSize();
+ pImpl->pSplitWin->MoveWindow( this, pImpl->aSplitSize, pImpl->nDockLine, pImpl->nDockPos, pImpl->bNewLine );
+ }
+ }
+ else
+ {
+ ResizableDockingWindow::EndDocking(rRect, bFloatMode);
+ }
+
+ SetAlignment( IsFloatingMode() ? SfxChildAlignment::NOALIGNMENT : pImpl->GetDockAlignment() );
+}
+
+/* [Description]
+
+ Virtual method of the DockingWindow class. Here, the interactive resize in
+ FloatingMode can be influenced, for example by only allowing for discrete
+ values for width and / or height. The base implementation prevents that the
+ output size is smaller than one set with SetMinOutputSizePixel().
+*/
+void SfxDockingWindow::Resizing( Size& /*rSize*/ )
+{
+
+}
+
+/* [Description]
+
+ Constructor for the SfxDockingWindow class. A SfxChildWindow will be
+ required because the docking is implemented in Sfx through SfxChildWindows.
+*/
+SfxDockingWindow::SfxDockingWindow( SfxBindings *pBindinx, SfxChildWindow *pCW,
+ vcl::Window* pParent, WinBits nWinBits)
+ : ResizableDockingWindow(pParent, nWinBits)
+ , pBindings(pBindinx)
+ , pMgr(pCW)
+{
+ pImpl.reset(new SfxDockingWindow_Impl(this));
+}
+
+/** Constructor for the SfxDockingWindow class. A SfxChildWindow will be
+ required because the docking is implemented in Sfx through SfxChildWindows.
+*/
+SfxDockingWindow::SfxDockingWindow( SfxBindings *pBindinx, SfxChildWindow *pCW,
+ vcl::Window* pParent, const OString& rID, const OUString& rUIXMLDescription)
+ : ResizableDockingWindow(pParent)
+ , pBindings(pBindinx)
+ , pMgr(pCW)
+{
+ m_xBuilder = Application::CreateInterimBuilder(m_xBox, rUIXMLDescription, true);
+ m_xContainer = m_xBuilder->weld_box(rID);
+
+ pImpl.reset(new SfxDockingWindow_Impl(this));
+}
+
+/** Initialization of the SfxDockingDialog class via a SfxChildWinInfo.
+ The initialization is done only in a 2nd step after the constructor, this
+ constructor should be called from the derived class or from the
+ SfxChildWindows.
+*/
+void SfxDockingWindow::Initialize(SfxChildWinInfo *pInfo)
+{
+ if ( !pMgr )
+ {
+ pImpl->SetDockAlignment( SfxChildAlignment::NOALIGNMENT );
+ pImpl->bConstructed = true;
+ return;
+ }
+
+ if (pInfo && (pInfo->nFlags & SfxChildWindowFlags::FORCEDOCK))
+ pImpl->bDockingPrevented = true;
+
+ pImpl->aSplitSize = GetOutputSizePixel();
+ if ( !GetFloatingSize().Width() )
+ {
+ Size aMinSize( GetMinOutputSizePixel() );
+ SetFloatingSize( pImpl->aSplitSize );
+ if ( pImpl->aSplitSize.Width() < aMinSize.Width() )
+ pImpl->aSplitSize.setWidth( aMinSize.Width() );
+ if ( pImpl->aSplitSize.Height() < aMinSize.Height() )
+ pImpl->aSplitSize.setHeight( aMinSize.Height() );
+ }
+
+ bool bVertHorzRead( false );
+ if (pInfo && !pInfo->aExtraString.isEmpty())
+ {
+ // get information about alignment, split size and position in SplitWindow
+ OUString aStr;
+ sal_Int32 nPos = pInfo->aExtraString.indexOf("AL:");
+ if ( nPos != -1 )
+ {
+ // alignment information
+ sal_Int32 n1 = pInfo->aExtraString.indexOf('(', nPos);
+ if ( n1 != -1 )
+ {
+ sal_Int32 n2 = pInfo->aExtraString.indexOf(')', n1);
+ if ( n2 != -1 )
+ {
+ // extract alignment information from extrastring
+ aStr = pInfo->aExtraString.copy(nPos, n2 - nPos + 1);
+ pInfo->aExtraString = pInfo->aExtraString.replaceAt(nPos, n2 - nPos + 1, u"");
+ aStr = aStr.replaceAt(nPos, n1-nPos+1, u"");
+ }
+ }
+ }
+
+ if ( !aStr.isEmpty() )
+ {
+ // accept window state only if alignment is also set
+ pImpl->aWinState = pInfo->aWinState;
+
+ // check for valid alignment
+ SfxChildAlignment eLocalAlignment = static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32()));
+ bool bIgnoreFloatConfig = (eLocalAlignment == SfxChildAlignment::NOALIGNMENT &&
+ !StyleSettings::GetDockingFloatsSupported());
+ if (pImpl->bDockingPrevented || bIgnoreFloatConfig)
+ // docking prevented, ignore old configuration and take alignment from default
+ aStr.clear();
+ else
+ SetAlignment( eLocalAlignment );
+
+ SfxChildAlignment eAlign = CheckAlignment(GetAlignment(),GetAlignment());
+ if ( eAlign != GetAlignment() )
+ {
+ OSL_FAIL("Invalid Alignment!");
+ SetAlignment( eAlign );
+ aStr.clear();
+ }
+
+ // get last alignment (for toggling)
+ nPos = aStr.indexOf(',');
+ if ( nPos != -1 )
+ {
+ aStr = aStr.copy(nPos+1);
+ pImpl->SetLastAlignment( static_cast<SfxChildAlignment>(static_cast<sal_uInt16>(aStr.toInt32())) );
+ }
+
+ nPos = aStr.indexOf(',');
+ if ( nPos != -1 )
+ {
+ // get split size and position in SplitWindow
+ Point aPos;
+ aStr = aStr.copy(nPos+1);
+ if ( GetPosSizeFromString( aStr, aPos, pImpl->aSplitSize ) )
+ {
+ pImpl->nLine = pImpl->nDockLine = static_cast<sal_uInt16>(aPos.X());
+ pImpl->nPos = pImpl->nDockPos = static_cast<sal_uInt16>(aPos.Y());
+ pImpl->nVerticalSize = pImpl->aSplitSize.Height();
+ pImpl->nHorizontalSize = pImpl->aSplitSize.Width();
+ if ( GetSplitSizeFromString( aStr, pImpl->aSplitSize ))
+ bVertHorzRead = true;
+ }
+ }
+ }
+ else {
+ OSL_FAIL( "Information is missing!" );
+ }
+ }
+
+ if ( !bVertHorzRead )
+ {
+ pImpl->nVerticalSize = pImpl->aSplitSize.Height();
+ pImpl->nHorizontalSize = pImpl->aSplitSize.Width();
+ }
+
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ if ( GetAlignment() != SfxChildAlignment::NOALIGNMENT )
+ {
+ // check if SfxWorkWindow is able to allow docking at its border
+ if (
+ !pWorkWin->IsDockingAllowed() ||
+ !pWorkWin->IsInternalDockingAllowed() ||
+ ( (GetFloatStyle() & WB_STANDALONE) && Application::IsInModalMode()) )
+ {
+ SetAlignment( SfxChildAlignment::NOALIGNMENT );
+ }
+ }
+
+ // detect floating mode
+ // toggling mode will not execute code in handlers, because pImpl->bConstructed is not set yet
+ bool bFloatMode = IsFloatingMode();
+ if ( bFloatMode != (GetAlignment() == SfxChildAlignment::NOALIGNMENT) )
+ {
+ bFloatMode = !bFloatMode;
+ SetFloatingMode( bFloatMode );
+ if ( bFloatMode )
+ {
+ if ( !pImpl->aWinState.isEmpty() )
+ GetFloatingWindow()->SetWindowState( pImpl->aWinState );
+ else
+ GetFloatingWindow()->SetOutputSizePixel( GetFloatingSize() );
+ }
+ }
+
+ if ( IsFloatingMode() )
+ {
+ // validate last alignment
+ SfxChildAlignment eLastAlign = pImpl->GetLastAlignment();
+ if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
+ eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::LEFT);
+ if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
+ eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::RIGHT);
+ if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
+ eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::TOP);
+ if ( eLastAlign == SfxChildAlignment::NOALIGNMENT)
+ eLastAlign = CheckAlignment(eLastAlign, SfxChildAlignment::BOTTOM);
+ pImpl->SetLastAlignment(eLastAlign);
+ }
+ else
+ {
+ // docked window must have NOALIGNMENT as last alignment
+ pImpl->SetLastAlignment(SfxChildAlignment::NOALIGNMENT);
+
+ pImpl->pSplitWin = pWorkWin->GetSplitWindow_Impl(GetAlignment());
+ pImpl->pSplitWin->InsertWindow(this, pImpl->aSplitSize);
+ }
+
+ // save alignment
+ pImpl->SetDockAlignment( GetAlignment() );
+}
+
+void SfxDockingWindow::Initialize_Impl()
+{
+ if ( !pMgr )
+ {
+ pImpl->bConstructed = true;
+ return;
+ }
+
+ SystemWindow* pFloatWin = GetFloatingWindow();
+ bool bSet = false;
+ if ( pFloatWin )
+ {
+ bSet = !pFloatWin->IsDefaultPos();
+ }
+ else
+ {
+ Point aPos = GetFloatingPos();
+ if ( aPos != Point() )
+ bSet = true;
+ }
+
+ if ( !bSet)
+ {
+ SfxViewFrame *pFrame = pBindings->GetDispatcher_Impl()->GetFrame();
+ vcl::Window* pEditWin = pFrame->GetViewShell()->GetWindow();
+ Point aPos = pEditWin->OutputToScreenPixel( pEditWin->GetPosPixel() );
+ aPos = GetParent()->ScreenToOutputPixel( aPos );
+ SetFloatingPos( aPos );
+ }
+
+ if ( pFloatWin )
+ {
+ // initialize floating window
+ if ( pImpl->aWinState.isEmpty() )
+ // window state never set before, get if from defaults
+ pImpl->aWinState = pFloatWin->GetWindowState();
+
+ // trick: use VCL method SetWindowState to adjust position and size
+ pFloatWin->SetWindowState( pImpl->aWinState );
+ Size aSize(pFloatWin->GetSizePixel());
+
+ // remember floating size for calculating alignment and tracking rectangle
+ SetFloatingSize(aSize);
+
+ }
+
+ // allow calling of docking handlers
+ pImpl->bConstructed = true;
+}
+
+/** Fills a SfxChildWinInfo with specific data from SfxDockingWindow,
+ so that it can be written in the INI file. It is assumed that rinfo
+ receives all other possible relevant data in the ChildWindow class.
+ Insertions are marked with size and the ZoomIn flag.
+ If this method is overridden, the base implementation must be called first.
+*/
+void SfxDockingWindow::FillInfo(SfxChildWinInfo& rInfo) const
+{
+ if (!pMgr || !pImpl)
+ return;
+
+ if (GetFloatingWindow() && pImpl->bConstructed)
+ pImpl->aWinState = GetFloatingWindow()->GetWindowState();
+
+ rInfo.aWinState = pImpl->aWinState;
+ rInfo.aExtraString = "AL:(";
+ rInfo.aExtraString += OUString::number(static_cast<sal_uInt16>(GetAlignment()));
+ rInfo.aExtraString += ",";
+ rInfo.aExtraString += OUString::number (static_cast<sal_uInt16>(pImpl->GetLastAlignment()));
+
+ Point aPos(pImpl->nLine, pImpl->nPos);
+ rInfo.aExtraString += ",";
+ rInfo.aExtraString += OUString::number( aPos.X() );
+ rInfo.aExtraString += "/";
+ rInfo.aExtraString += OUString::number( aPos.Y() );
+ rInfo.aExtraString += "/";
+ rInfo.aExtraString += OUString::number( pImpl->nHorizontalSize );
+ rInfo.aExtraString += "/";
+ rInfo.aExtraString += OUString::number( pImpl->nVerticalSize );
+ rInfo.aExtraString += ",";
+ rInfo.aExtraString += OUString::number( pImpl->aSplitSize.Width() );
+ rInfo.aExtraString += ";";
+ rInfo.aExtraString += OUString::number( pImpl->aSplitSize.Height() );
+
+ rInfo.aExtraString += ")";
+}
+
+SfxDockingWindow::~SfxDockingWindow()
+{
+ disposeOnce();
+}
+
+void SfxDockingWindow::dispose()
+{
+ ReleaseChildWindow_Impl();
+ pImpl.reset();
+ m_xContainer.reset();
+ m_xBuilder.reset();
+ ResizableDockingWindow::dispose();
+}
+
+void SfxDockingWindow::ReleaseChildWindow_Impl()
+{
+ if ( pMgr && pMgr->GetFrame() == pBindings->GetActiveFrame() )
+ pBindings->SetActiveFrame( nullptr );
+
+ if ( pMgr && pImpl->pSplitWin && pImpl->pSplitWin->IsItemValid( GetType() ) )
+ pImpl->pSplitWin->RemoveWindow(this);
+
+ pMgr=nullptr;
+}
+
+/** This method calculates a resulting alignment for the given mouse position
+ and tracking rectangle. When changing the alignment it can also be that
+ the tracking rectangle is changed, so that an altered rectangle is
+ returned. The user of this class can influence behaviour of this method,
+ and thus the behavior of his DockinWindow class when docking where the
+ called virtual method:
+
+ SfxDockingWindow::CalcDockingSize (SfxChildAlignment eAlign)
+
+ is overridden (see below).
+*/
+SfxChildAlignment SfxDockingWindow::CalcAlignment(const Point& rPos, tools::Rectangle& rRect)
+{
+ // calculate hypothetical sizes for different modes
+ Size aFloatingSize(CalcDockingSize(SfxChildAlignment::NOALIGNMENT));
+
+ // check if docking is permitted
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ if ( !pWorkWin->IsDockingAllowed() )
+ {
+ rRect.SetSize( aFloatingSize );
+ return pImpl->GetDockAlignment();
+ }
+
+ // calculate borders to shrink inner area before checking for intersection with tracking rectangle
+ tools::Long nLRBorder, nTBBorder;
+
+ // take the smaller size of docked and floating mode
+ Size aBorderTmp = pImpl->aSplitSize;
+ if ( GetFloatingSize().Height() < aBorderTmp.Height() )
+ aBorderTmp.setHeight( GetFloatingSize().Height() );
+ if ( GetFloatingSize().Width() < aBorderTmp.Width() )
+ aBorderTmp.setWidth( GetFloatingSize().Width() );
+
+ nLRBorder = aBorderTmp.Width();
+ nTBBorder = aBorderTmp.Height();
+
+ // limit border to predefined constant values
+ if ( nLRBorder > MAX_TOGGLEAREA_WIDTH )
+ nLRBorder = MAX_TOGGLEAREA_WIDTH;
+ if ( nTBBorder > MAX_TOGGLEAREA_WIDTH )
+ nTBBorder = MAX_TOGGLEAREA_WIDTH;
+
+ // shrink area for floating mode if possible
+ tools::Rectangle aInRect = GetInnerRect();
+ if ( aInRect.GetWidth() > nLRBorder )
+ aInRect.AdjustLeft(nLRBorder/2 );
+ if ( aInRect.GetWidth() > nLRBorder )
+ aInRect.AdjustRight( -(nLRBorder/2) );
+ if ( aInRect.GetHeight() > nTBBorder )
+ aInRect.AdjustTop(nTBBorder/2 );
+ if ( aInRect.GetHeight() > nTBBorder )
+ aInRect.AdjustBottom( -(nTBBorder/2) );
+
+ // calculate alignment resulting from docking rectangle
+ bool bBecomesFloating = false;
+ SfxChildAlignment eDockAlign = pImpl->GetDockAlignment();
+ tools::Rectangle aDockingRect( rRect );
+ if ( !IsFloatingMode() )
+ {
+ // don't use tracking rectangle for alignment check, because it will be too large
+ // to get a floating mode as result - switch to floating size
+ // so the calculation only depends on the position of the rectangle, not the current
+ // docking state of the window
+ aDockingRect.SetSize( GetFloatingSize() );
+
+ // in this mode docking is never done by keyboard, so it's OK to use the mouse position
+ aDockingRect.SetPos( pWorkWin->GetWindow()->OutputToScreenPixel( pWorkWin->GetWindow()->GetPointerPosPixel() ) );
+ }
+
+ Point aPos = aDockingRect.TopLeft();
+ tools::Rectangle aIntersect = GetOuterRect().GetIntersection( aDockingRect );
+ if ( aIntersect.IsEmpty() )
+ // docking rectangle completely outside docking area -> floating mode
+ bBecomesFloating = true;
+ else
+ {
+ // create a small test rect around the mouse position and use this one
+ // instead of the passed rRect to not dock too easily or by accident
+ tools::Rectangle aSmallDockingRect;
+ aSmallDockingRect.SetSize( Size( MAX_TOGGLEAREA_WIDTH, MAX_TOGGLEAREA_HEIGHT ) );
+ Point aNewPos(rPos);
+ aNewPos.AdjustX( -(aSmallDockingRect.GetWidth()/2) );
+ aNewPos.AdjustY( -(aSmallDockingRect.GetHeight()/2) );
+ aSmallDockingRect.SetPos(aNewPos);
+ tools::Rectangle aIntersectRect = aInRect.GetIntersection( aSmallDockingRect );
+ if ( aIntersectRect == aSmallDockingRect )
+ // docking rectangle completely inside (shrunk) inner area -> floating mode
+ bBecomesFloating = true;
+ }
+
+ if ( bBecomesFloating )
+ {
+ eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::NOALIGNMENT);
+ }
+ else
+ {
+ // docking rectangle is in the "sensible area"
+ Point aInPosTL( aPos.X()-aInRect.Left(), aPos.Y()-aInRect.Top() );
+ Point aInPosBR( aPos.X()-aInRect.Left() + aDockingRect.GetWidth(), aPos.Y()-aInRect.Top() + aDockingRect.GetHeight() );
+ Size aInSize = aInRect.GetSize();
+ bool bNoChange = false;
+
+ // check if alignment is still unchanged
+ switch ( GetAlignment() )
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::FIRSTLEFT:
+ case SfxChildAlignment::LASTLEFT:
+ if (aInPosTL.X() <= 0)
+ {
+ eDockAlign = GetAlignment();
+ bNoChange = true;
+ }
+ break;
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::LOWESTTOP:
+ case SfxChildAlignment::HIGHESTTOP:
+ if ( aInPosTL.Y() <= 0)
+ {
+ eDockAlign = GetAlignment();
+ bNoChange = true;
+ }
+ break;
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::FIRSTRIGHT:
+ case SfxChildAlignment::LASTRIGHT:
+ if ( aInPosBR.X() >= aInSize.Width())
+ {
+ eDockAlign = GetAlignment();
+ bNoChange = true;
+ }
+ break;
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::LOWESTBOTTOM:
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ if ( aInPosBR.Y() >= aInSize.Height())
+ {
+ eDockAlign = GetAlignment();
+ bNoChange = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ( !bNoChange )
+ {
+ // alignment will change, test alignment according to distance of the docking rectangles edges
+ bool bForbidden = true;
+ if ( aInPosTL.X() <= 0)
+ {
+ eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::LEFT);
+ bForbidden = ( eDockAlign != SfxChildAlignment::LEFT &&
+ eDockAlign != SfxChildAlignment::FIRSTLEFT &&
+ eDockAlign != SfxChildAlignment::LASTLEFT );
+ }
+
+ if ( bForbidden && aInPosTL.Y() <= 0)
+ {
+ eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::TOP);
+ bForbidden = ( eDockAlign != SfxChildAlignment::TOP &&
+ eDockAlign != SfxChildAlignment::HIGHESTTOP &&
+ eDockAlign != SfxChildAlignment::LOWESTTOP );
+ }
+
+ if ( bForbidden && aInPosBR.X() >= aInSize.Width())
+ {
+ eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::RIGHT);
+ bForbidden = ( eDockAlign != SfxChildAlignment::RIGHT &&
+ eDockAlign != SfxChildAlignment::FIRSTRIGHT &&
+ eDockAlign != SfxChildAlignment::LASTRIGHT );
+ }
+
+ if ( bForbidden && aInPosBR.Y() >= aInSize.Height())
+ {
+ eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::BOTTOM);
+ bForbidden = ( eDockAlign != SfxChildAlignment::BOTTOM &&
+ eDockAlign != SfxChildAlignment::HIGHESTBOTTOM &&
+ eDockAlign != SfxChildAlignment::LOWESTBOTTOM );
+ }
+
+ // the calculated alignment was rejected by the window -> take floating mode
+ if ( bForbidden )
+ eDockAlign = CheckAlignment(pImpl->GetDockAlignment(),SfxChildAlignment::NOALIGNMENT);
+ }
+ }
+
+ if ( eDockAlign == SfxChildAlignment::NOALIGNMENT )
+ {
+ // In the FloatingMode the tracking rectangle will get the floating
+ // size. Due to a bug the rRect may only be changed when the
+ // alignment is changed!
+ if ( eDockAlign != pImpl->GetDockAlignment() )
+ aDockingRect.SetSize( aFloatingSize );
+ }
+ else
+ {
+ sal_uInt16 nLine, nPos;
+ SfxSplitWindow *pSplitWin = pWorkWin->GetSplitWindow_Impl(eDockAlign);
+ aPos = pSplitWin->ScreenToOutputPixel( aPos );
+ if ( pSplitWin->GetWindowPos( aPos, nLine, nPos ) )
+ {
+ // mouse over splitwindow, get line and position
+ pImpl->nDockLine = nLine;
+ pImpl->nDockPos = nPos;
+ pImpl->bNewLine = false;
+ }
+ else
+ {
+ // mouse touches inner border -> create new line
+ if ( eDockAlign == GetAlignment() && pImpl->pSplitWin &&
+ pImpl->nLine == pImpl->pSplitWin->GetLineCount()-1 && pImpl->pSplitWin->GetWindowCount(pImpl->nLine) == 1 )
+ {
+ // if this window is the only one in the last line, it can't be docked as new line in the same splitwindow
+ pImpl->nDockLine = pImpl->nLine;
+ pImpl->nDockPos = pImpl->nPos;
+ pImpl->bNewLine = false;
+ }
+ else
+ {
+ // create new line
+ pImpl->nDockLine = pSplitWin->GetLineCount();
+ pImpl->nDockPos = 0;
+ pImpl->bNewLine = true;
+ }
+ }
+
+ bool bChanged = pImpl->nLine != pImpl->nDockLine || pImpl->nPos != pImpl->nDockPos || eDockAlign != GetAlignment();
+ if ( !bChanged && !IsFloatingMode() )
+ {
+ // window only slightly moved, no change of any property
+ rRect.SetSize( pImpl->aSplitSize );
+ rRect.SetPos( aDockingRect.TopLeft() );
+ return eDockAlign;
+ }
+
+ // calculate new size and position
+ Size aSize;
+ Point aPoint = aDockingRect.TopLeft();
+ Size aInnerSize = GetInnerRect().GetSize();
+ if ( eDockAlign == SfxChildAlignment::LEFT || eDockAlign == SfxChildAlignment::RIGHT )
+ {
+ if ( pImpl->bNewLine )
+ {
+ // set height to height of free area
+ aSize.setHeight( aInnerSize.Height() );
+ aSize.setWidth( pImpl->nHorizontalSize );
+ if ( eDockAlign == SfxChildAlignment::LEFT )
+ {
+ aPoint = aInnerRect.TopLeft();
+ }
+ else
+ {
+ aPoint = aInnerRect.TopRight();
+ aPoint.AdjustX( -(aSize.Width()) );
+ }
+ }
+ else
+ {
+ // get width from splitwindow
+ aSize.setWidth( pSplitWin->GetLineSize(nLine) );
+ aSize.setHeight( pImpl->aSplitSize.Height() );
+ }
+ }
+ else
+ {
+ if ( pImpl->bNewLine )
+ {
+ // set width to width of free area
+ aSize.setWidth( aInnerSize.Width() );
+ aSize.setHeight( pImpl->nVerticalSize );
+ if ( eDockAlign == SfxChildAlignment::TOP )
+ {
+ aPoint = aInnerRect.TopLeft();
+ }
+ else
+ {
+ aPoint = aInnerRect.BottomLeft();
+ aPoint.AdjustY( -(aSize.Height()) );
+ }
+ }
+ else
+ {
+ // get height from splitwindow
+ aSize.setHeight( pSplitWin->GetLineSize(nLine) );
+ aSize.setWidth( pImpl->aSplitSize.Width() );
+ }
+ }
+
+ aDockingRect.SetSize( aSize );
+ aDockingRect.SetPos( aPoint );
+ }
+
+ rRect = aDockingRect;
+ return eDockAlign;
+}
+
+/** Virtual method of the SfxDockingWindow class. This method determines how
+ the size of the DockingWindows changes depending on the alignment. The base
+ implementation uses the floating mode, the size of the marked Floating
+ Size. For horizontal alignment, the width will be the width of the outer
+ DockingRectangle, with vertical alignment the height will be the height of
+ the inner DockingRectangle (resulting from the order in which the SFX child
+ windows are displayed). The other size is set to the current floating-size,
+ this could changed by a to intervening derived class. The docking size must
+ be the same for Left/Right and Top/Bottom.
+*/
+Size SfxDockingWindow::CalcDockingSize(SfxChildAlignment eAlign)
+{
+ // Note: if the resizing is also possible in the docked state, then the
+ // Floating-size does also have to be adjusted?
+
+ Size aSize = GetFloatingSize();
+ switch (eAlign)
+ {
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::LOWESTTOP:
+ case SfxChildAlignment::HIGHESTTOP:
+ case SfxChildAlignment::LOWESTBOTTOM:
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ aSize.setWidth( aOuterRect.Right() - aOuterRect.Left() );
+ break;
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::FIRSTLEFT:
+ case SfxChildAlignment::LASTLEFT:
+ case SfxChildAlignment::FIRSTRIGHT:
+ case SfxChildAlignment::LASTRIGHT:
+ aSize.setHeight( aInnerRect.Bottom() - aInnerRect.Top() );
+ break;
+ case SfxChildAlignment::NOALIGNMENT:
+ break;
+ default:
+ break;
+ }
+
+ return aSize;
+}
+
+/** Virtual method of the SfxDockingWindow class. Here a derived class can
+ disallow certain alignments. The base implementation does not
+ prohibit alignment.
+*/
+SfxChildAlignment SfxDockingWindow::CheckAlignment(SfxChildAlignment,
+ SfxChildAlignment eAlign)
+{
+ return eAlign;
+}
+
+/** The window is closed when the ChildWindow is destroyed by running the
+ ChildWindow-slots. If this is method is overridden by a derived class
+ method, then the SfxDockingDialogWindow: Close() must be called afterwards
+ if the Close() was not cancelled with "return sal_False".
+*/
+bool SfxDockingWindow::Close()
+{
+ // Execute with Parameters, since Toggle is ignored by some ChildWindows.
+ if ( !pMgr )
+ return true;
+
+ SfxBoolItem aValue( pMgr->GetType(), false);
+ pBindings->GetDispatcher_Impl()->ExecuteList(
+ pMgr->GetType(), SfxCallMode::RECORD | SfxCallMode::ASYNCHRON,
+ { &aValue });
+ return true;
+}
+
+void SfxDockingWindow::Paint(vcl::RenderContext&, const tools::Rectangle& /*rRect*/)
+{
+}
+
+/** With this method, a minimal OutputSize be can set, that is queried in
+ the Resizing()-Handler.
+*/
+void SfxDockingWindow::SetMinOutputSizePixel( const Size& rSize )
+{
+ pImpl->aMinSize = rSize;
+ ResizableDockingWindow::SetMinOutputSizePixel( rSize );
+}
+
+/** Set the minimum size which is returned.*/
+const Size& SfxDockingWindow::GetMinOutputSizePixel() const
+{
+ return pImpl->aMinSize;
+}
+
+bool SfxDockingWindow::EventNotify( NotifyEvent& rEvt )
+{
+ if ( !pImpl )
+ return ResizableDockingWindow::EventNotify( rEvt );
+
+ if ( rEvt.GetType() == MouseNotifyEvent::GETFOCUS )
+ {
+ if (pMgr != nullptr)
+ pBindings->SetActiveFrame( pMgr->GetFrame() );
+
+ if ( pImpl->pSplitWin )
+ pImpl->pSplitWin->SetActiveWindow_Impl( this );
+ else if (pMgr != nullptr)
+ pMgr->Activate_Impl();
+
+ // In VCL EventNotify goes first to the window itself, also call the
+ // base class, otherwise the parent learns nothing
+ // if ( rEvt.GetWindow() == this ) PB: #i74693# not necessary any longer
+ ResizableDockingWindow::EventNotify( rEvt );
+ return true;
+ }
+ else if( rEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ // First, allow KeyInput for Dialog functions
+ if (!DockingWindow::EventNotify(rEvt) && SfxViewShell::Current())
+ {
+ // then also for valid global accelerators.
+ return SfxViewShell::Current()->GlobalKeyInput_Impl( *rEvt.GetKeyEvent() );
+ }
+ return true;
+ }
+ else if ( rEvt.GetType() == MouseNotifyEvent::LOSEFOCUS && !HasChildPathFocus() )
+ {
+ pBindings->SetActiveFrame( nullptr );
+ }
+
+ return ResizableDockingWindow::EventNotify( rEvt );
+}
+
+void SfxDockingWindow::SetItemSize_Impl( const Size& rSize )
+{
+ pImpl->aSplitSize = rSize;
+
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::ALIGNDOCKINGWINDOW, pMgr->GetType() );
+}
+
+void SfxDockingWindow::Disappear_Impl()
+{
+ if ( pImpl->pSplitWin && pImpl->pSplitWin->IsItemValid( GetType() ) )
+ pImpl->pSplitWin->RemoveWindow(this);
+}
+
+void SfxDockingWindow::Reappear_Impl()
+{
+ if ( pImpl->pSplitWin && !pImpl->pSplitWin->IsItemValid( GetType() ) )
+ {
+ pImpl->pSplitWin->InsertWindow( this, pImpl->aSplitSize );
+ }
+}
+
+bool SfxDockingWindow::IsAutoHide_Impl() const
+{
+ if ( pImpl->pSplitWin )
+ return !pImpl->pSplitWin->IsFadeIn();
+ else
+ return false;
+}
+
+void SfxDockingWindow::AutoShow_Impl()
+{
+ if ( pImpl->pSplitWin )
+ {
+ pImpl->pSplitWin->FadeIn();
+ }
+}
+
+void SfxDockingWindow::StateChanged( StateChangedType nStateChange )
+{
+ if ( nStateChange == StateChangedType::InitShow )
+ Initialize_Impl();
+
+ ResizableDockingWindow::StateChanged( nStateChange );
+}
+
+void SfxDockingWindow::Move()
+{
+ if ( pImpl )
+ pImpl->aMoveIdle.Start();
+}
+
+IMPL_LINK_NOARG(SfxDockingWindow, TimerHdl, Timer *, void)
+{
+ pImpl->aMoveIdle.Stop();
+ if ( IsReallyVisible() && IsFloatingMode() )
+ {
+ SetFloatingSize( GetOutputSizePixel() );
+ pImpl->aWinState = GetFloatingWindow()->GetWindowState();
+ SfxWorkWindow *pWorkWin = pBindings->GetWorkWindow_Impl();
+ pWorkWin->ConfigChild_Impl( SfxChildIdentifier::SPLITWINDOW, SfxDockingConfig::ALIGNDOCKINGWINDOW, pMgr->GetType() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/documentfontsdialog.cxx b/sfx2/source/dialog/documentfontsdialog.cxx
new file mode 100644
index 000000000..e7c0348b5
--- /dev/null
+++ b/sfx2/source/dialog/documentfontsdialog.cxx
@@ -0,0 +1,113 @@
+/* -*- 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 <documentfontsdialog.hxx>
+
+#include <sfx2/objsh.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+using namespace ::com::sun::star;
+
+std::unique_ptr<SfxTabPage> SfxDocumentFontsPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* set)
+{
+ return std::make_unique<SfxDocumentFontsPage>(pPage, pController, *set);
+}
+
+SfxDocumentFontsPage::SfxDocumentFontsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& set)
+ : SfxTabPage(pPage, pController, "sfx/ui/documentfontspage.ui", "DocumentFontsPage", &set)
+ , embedFontsCheckbox(m_xBuilder->weld_check_button("embedFonts"))
+ , embedUsedFontsCheckbox(m_xBuilder->weld_check_button("embedUsedFonts"))
+ , embedLatinScriptFontsCheckbox(m_xBuilder->weld_check_button("embedLatinScriptFonts"))
+ , embedAsianScriptFontsCheckbox(m_xBuilder->weld_check_button("embedAsianScriptFonts"))
+ , embedComplexScriptFontsCheckbox(m_xBuilder->weld_check_button("embedComplexScriptFonts"))
+{
+}
+
+SfxDocumentFontsPage::~SfxDocumentFontsPage()
+{
+}
+
+void SfxDocumentFontsPage::Reset( const SfxItemSet* )
+{
+ bool bEmbedFonts = false;
+ bool bEmbedUsedFonts = false;
+
+ bool bEmbedLatinScriptFonts = false;
+ bool bEmbedAsianScriptFonts = false;
+ bool bEmbedComplexScriptFonts = false;
+
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if (pDocSh)
+ {
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFac( pDocSh->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW );
+
+ xProps->getPropertyValue("EmbedFonts") >>= bEmbedFonts;
+ xProps->getPropertyValue("EmbedOnlyUsedFonts") >>= bEmbedUsedFonts;
+ xProps->getPropertyValue("EmbedLatinScriptFonts") >>= bEmbedLatinScriptFonts;
+ xProps->getPropertyValue("EmbedAsianScriptFonts") >>= bEmbedAsianScriptFonts;
+ xProps->getPropertyValue("EmbedComplexScriptFonts") >>= bEmbedComplexScriptFonts;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ embedFontsCheckbox->set_active(bEmbedFonts);
+ embedUsedFontsCheckbox->set_active(bEmbedUsedFonts);
+
+ embedLatinScriptFontsCheckbox->set_active(bEmbedLatinScriptFonts);
+ embedAsianScriptFontsCheckbox->set_active(bEmbedAsianScriptFonts);
+ embedComplexScriptFontsCheckbox->set_active(bEmbedComplexScriptFonts);
+}
+
+bool SfxDocumentFontsPage::FillItemSet( SfxItemSet* )
+{
+ bool bEmbedFonts = embedFontsCheckbox->get_active();
+ bool bEmbedUsedFonts = embedUsedFontsCheckbox->get_active();
+
+ bool bEmbedLatinScriptFonts = embedLatinScriptFontsCheckbox->get_active();
+ bool bEmbedAsianScriptFonts = embedAsianScriptFontsCheckbox->get_active();
+ bool bEmbedComplexScriptFonts = embedComplexScriptFontsCheckbox->get_active();
+
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if ( pDocSh )
+ {
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFac( pDocSh->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xFac->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue("EmbedFonts", uno::Any(bEmbedFonts));
+ xProps->setPropertyValue("EmbedOnlyUsedFonts", uno::Any(bEmbedUsedFonts));
+ xProps->setPropertyValue("EmbedLatinScriptFonts", uno::Any(bEmbedLatinScriptFonts));
+ xProps->setPropertyValue("EmbedAsianScriptFonts", uno::Any(bEmbedAsianScriptFonts));
+ xProps->setPropertyValue("EmbedComplexScriptFonts", uno::Any(bEmbedComplexScriptFonts));
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/filedlghelper.cxx b/sfx2/source/dialog/filedlghelper.cxx
new file mode 100644
index 000000000..859c5663f
--- /dev/null
+++ b/sfx2/source/dialog/filedlghelper.cxx
@@ -0,0 +1,3000 @@
+/* -*- 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 <optional>
+#include <string_view>
+
+#include <sfx2/filedlghelper.hxx>
+#include <sal/types.h>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ControlActions.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp>
+#include <com/sun/star/ui/dialogs/FolderPicker.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XControlInformation.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePreview.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/util/RevisionTag.hpp>
+#include <comphelper/fileurl.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/help.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/saveopt.hxx>
+#include <unotools/securityoptions.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <unotools/viewoptions.hxx>
+#include <svtools/helpids.h>
+#include <comphelper/docpasswordrequest.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/sfxsids.hrc>
+#include "filtergrouping.hxx"
+#include "filedlgimpl.hxx"
+#include <sfx2/strings.hrc>
+#include <sal/log.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#ifdef UNX
+#include <errno.h>
+#include <sys/stat.h>
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::ui::dialogs::TemplateDescription;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::cppu;
+
+constexpr OUStringLiteral IODLG_CONFIGNAME = u"FilePicker_Save";
+constexpr OUStringLiteral IMPGRF_CONFIGNAME = u"FilePicker_Graph";
+constexpr OUStringLiteral USERITEM_NAME = u"UserItem";
+
+namespace sfx2
+{
+
+namespace
+{
+ bool lclSupportsOOXMLEncryption(std::u16string_view aFilterName)
+ {
+ return aFilterName == u"Calc MS Excel 2007 XML"
+ || aFilterName == u"MS Word 2007 XML"
+ || aFilterName == u"Impress MS PowerPoint 2007 XML"
+ || aFilterName == u"Impress MS PowerPoint 2007 XML AutoPlay"
+ || aFilterName == u"Calc Office Open XML"
+ || aFilterName == u"Impress Office Open XML"
+ || aFilterName == u"Impress Office Open XML AutoPlay"
+ || aFilterName == u"Office Open XML Text";
+ }
+}
+
+static std::optional<OUString> GetLastFilterConfigId( FileDialogHelper::Context _eContext )
+{
+ static constexpr OUStringLiteral aSD_EXPORT_IDENTIFIER(u"SdExportLastFilter");
+ static constexpr OUStringLiteral aSI_EXPORT_IDENTIFIER(u"SiExportLastFilter");
+ static constexpr OUStringLiteral aSW_EXPORT_IDENTIFIER(u"SwExportLastFilter");
+
+ switch( _eContext )
+ {
+ case FileDialogHelper::DrawExport: return aSD_EXPORT_IDENTIFIER;
+ case FileDialogHelper::ImpressExport: return aSI_EXPORT_IDENTIFIER;
+ case FileDialogHelper::WriterExport: return aSW_EXPORT_IDENTIFIER;
+ default: break;
+ }
+
+ return {};
+}
+
+static OUString EncodeSpaces_Impl( const OUString& rSource );
+static OUString DecodeSpaces_Impl( const OUString& rSource );
+
+// FileDialogHelper_Impl
+
+// XFilePickerListener Methods
+void SAL_CALL FileDialogHelper_Impl::fileSelectionChanged( const FilePickerEvent& )
+{
+ SolarMutexGuard aGuard;
+ mpAntiImpl->FileSelectionChanged();
+}
+
+void SAL_CALL FileDialogHelper_Impl::directoryChanged( const FilePickerEvent& )
+{
+ SolarMutexGuard aGuard;
+ mpAntiImpl->DirectoryChanged();
+}
+
+OUString SAL_CALL FileDialogHelper_Impl::helpRequested( const FilePickerEvent& aEvent )
+{
+ SolarMutexGuard aGuard;
+ return sfx2::FileDialogHelper::HelpRequested( aEvent );
+}
+
+void SAL_CALL FileDialogHelper_Impl::controlStateChanged( const FilePickerEvent& aEvent )
+{
+ SolarMutexGuard aGuard;
+ mpAntiImpl->ControlStateChanged( aEvent );
+}
+
+void SAL_CALL FileDialogHelper_Impl::dialogSizeChanged()
+{
+ SolarMutexGuard aGuard;
+ mpAntiImpl->DialogSizeChanged();
+}
+
+// XDialogClosedListener Methods
+void SAL_CALL FileDialogHelper_Impl::dialogClosed( const DialogClosedEvent& _rEvent )
+{
+ SolarMutexGuard aGuard;
+ mpAntiImpl->DialogClosed( _rEvent );
+ postExecute( _rEvent.DialogResult );
+}
+
+// handle XFilePickerListener events
+void FileDialogHelper_Impl::handleFileSelectionChanged()
+{
+ if ( mbHasVersions )
+ updateVersions();
+
+ if ( mbShowPreview )
+ maPreviewIdle.Start();
+}
+
+void FileDialogHelper_Impl::handleDirectoryChanged()
+{
+ if ( mbShowPreview )
+ TimeOutHdl_Impl( nullptr );
+}
+
+OUString FileDialogHelper_Impl::handleHelpRequested( const FilePickerEvent& aEvent )
+{
+ //!!! todo: cache the help strings (here or TRA)
+
+ OString sHelpId;
+ // mapping from element id -> help id
+ switch ( aEvent.ElementId )
+ {
+ case ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION :
+ sHelpId = HID_FILESAVE_AUTOEXTENSION;
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_PASSWORD :
+ sHelpId = HID_FILESAVE_SAVEWITHPASSWORD;
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS :
+ sHelpId = HID_FILESAVE_CUSTOMIZEFILTER;
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_READONLY :
+ sHelpId = HID_FILEOPEN_READONLY;
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_LINK :
+ sHelpId = HID_FILEDLG_LINK_CB;
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW :
+ sHelpId = HID_FILEDLG_PREVIEW_CB;
+ break;
+
+ case ExtendedFilePickerElementIds::PUSHBUTTON_PLAY :
+ sHelpId = HID_FILESAVE_DOPLAY;
+ break;
+
+ case ExtendedFilePickerElementIds::LISTBOX_VERSION_LABEL :
+ case ExtendedFilePickerElementIds::LISTBOX_VERSION :
+ sHelpId = HID_FILEOPEN_VERSION;
+ break;
+
+ case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE_LABEL :
+ case ExtendedFilePickerElementIds::LISTBOX_TEMPLATE :
+ sHelpId = HID_FILESAVE_TEMPLATE;
+ break;
+
+ case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE_LABEL :
+ case ExtendedFilePickerElementIds::LISTBOX_IMAGE_TEMPLATE :
+ sHelpId = HID_FILEOPEN_IMAGE_TEMPLATE;
+ break;
+
+ case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR_LABEL :
+ case ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR :
+ sHelpId = HID_FILEOPEN_IMAGE_ANCHOR;
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_SELECTION :
+ sHelpId = HID_FILESAVE_SELECTION;
+ break;
+
+ default:
+ SAL_WARN( "sfx.dialog", "invalid element id" );
+ }
+
+ OUString aHelpText;
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ aHelpText = pHelp->GetHelpText(OStringToOUString(sHelpId, RTL_TEXTENCODING_UTF8), static_cast<weld::Widget*>(nullptr));
+ return aHelpText;
+}
+
+void FileDialogHelper_Impl::handleControlStateChanged( const FilePickerEvent& aEvent )
+{
+ switch ( aEvent.ElementId )
+ {
+ case CommonFilePickerElementIds::LISTBOX_FILTER:
+ updateFilterOptionsBox();
+ enablePasswordBox( false );
+ updateSelectionBox();
+ // only use it for export and with our own dialog
+ if ( mbExport && !mbSystemPicker )
+ updateExportButton();
+ break;
+
+ case ExtendedFilePickerElementIds::CHECKBOX_PREVIEW:
+ updatePreviewState(true);
+ break;
+ }
+}
+
+void FileDialogHelper_Impl::handleDialogSizeChanged()
+{
+ if ( mbShowPreview )
+ TimeOutHdl_Impl( nullptr );
+}
+
+// XEventListener Methods
+void SAL_CALL FileDialogHelper_Impl::disposing( const EventObject& )
+{
+ SolarMutexGuard aGuard;
+ dispose();
+}
+
+void FileDialogHelper_Impl::dispose()
+{
+ if ( mxFileDlg.is() )
+ {
+ // remove the event listener
+ mxFileDlg->removeFilePickerListener( this );
+
+ ::comphelper::disposeComponent( mxFileDlg );
+ mxFileDlg.clear();
+ }
+}
+
+OUString FileDialogHelper_Impl::getCurrentFilterUIName() const
+{
+ OUString aFilterName;
+
+ if( mxFileDlg.is() )
+ {
+ aFilterName = mxFileDlg->getCurrentFilter();
+
+ if ( !aFilterName.isEmpty() && isShowFilterExtensionEnabled() )
+ aFilterName = getFilterName( aFilterName );
+ }
+
+ return aFilterName;
+}
+
+void FileDialogHelper_Impl::LoadLastUsedFilter( const OUString& _rContextIdentifier )
+{
+ SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME );
+
+ if( aDlgOpt.Exists() )
+ {
+ OUString aLastFilter;
+ if( aDlgOpt.GetUserItem( _rContextIdentifier ) >>= aLastFilter )
+ setFilter( aLastFilter );
+ }
+}
+
+void FileDialogHelper_Impl::SaveLastUsedFilter()
+{
+ std::optional<OUString> pConfigId = GetLastFilterConfigId( meContext );
+ if( pConfigId )
+ SvtViewOptions( EViewType::Dialog, IODLG_CONFIGNAME ).SetUserItem( *pConfigId,
+ Any( getFilterWithExtension( getFilter() ) ) );
+}
+
+std::shared_ptr<const SfxFilter> FileDialogHelper_Impl::getCurentSfxFilter()
+{
+ OUString aFilterName = getCurrentFilterUIName();
+
+ if ( mpMatcher && !aFilterName.isEmpty() )
+ return mpMatcher->GetFilter4UIName( aFilterName, m_nMustFlags, m_nDontFlags );
+
+ return nullptr;
+}
+
+bool FileDialogHelper_Impl::updateExtendedControl( sal_Int16 _nExtendedControlId, bool _bEnable )
+{
+ bool bIsEnabled = false;
+
+ uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+ if ( xCtrlAccess.is() )
+ {
+ try
+ {
+ xCtrlAccess->enableControl( _nExtendedControlId, _bEnable );
+ bIsEnabled = _bEnable;
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::updateExtendedControl" );
+ }
+ }
+ return bIsEnabled;
+}
+
+bool FileDialogHelper_Impl::CheckFilterOptionsCapability( const std::shared_ptr<const SfxFilter>& _pFilter )
+{
+ bool bResult = false;
+
+ if( mxFilterCFG.is() && _pFilter )
+ {
+ try
+ {
+ Sequence < PropertyValue > aProps;
+ Any aAny = mxFilterCFG->getByName( _pFilter->GetName() );
+ if ( aAny >>= aProps )
+ {
+ OUString aServiceName;
+ for( const auto& rProp : std::as_const(aProps) )
+ {
+ if( rProp.Name == "UIComponent" )
+ {
+ rProp.Value >>= aServiceName;
+ if( !aServiceName.isEmpty() )
+ bResult = true;
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+
+ return bResult;
+}
+
+bool FileDialogHelper_Impl::isInOpenMode() const
+{
+ bool bRet = false;
+
+ switch ( m_nDialogType )
+ {
+ case FILEOPEN_SIMPLE:
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ case FILEOPEN_PLAY:
+ case FILEOPEN_LINK_PLAY:
+ case FILEOPEN_READONLY_VERSION:
+ case FILEOPEN_LINK_PREVIEW:
+ case FILEOPEN_PREVIEW:
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void FileDialogHelper_Impl::updateFilterOptionsBox()
+{
+ if ( !m_bHaveFilterOptions )
+ return;
+
+ updateExtendedControl(
+ ExtendedFilePickerElementIds::CHECKBOX_FILTEROPTIONS,
+ CheckFilterOptionsCapability( getCurentSfxFilter() )
+ );
+}
+
+void FileDialogHelper_Impl::updateExportButton()
+{
+ uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+ if ( !xCtrlAccess.is() )
+ return;
+
+ OUString sOldLabel( xCtrlAccess->getLabel( CommonFilePickerElementIds::PUSHBUTTON_OK ) );
+
+ // initialize button label; we need the label with the mnemonic char
+ if ( maButtonLabel.isEmpty() || maButtonLabel.indexOf( MNEMONIC_CHAR ) == -1 )
+ {
+ // cut the ellipses, if necessary
+ sal_Int32 nIndex = sOldLabel.indexOf( "..." );
+ if ( -1 == nIndex )
+ nIndex = sOldLabel.getLength();
+ maButtonLabel = sOldLabel.copy( 0, nIndex );
+ }
+
+ OUString sLabel = maButtonLabel;
+ // filter with options -> append ellipses on export button label
+ if ( CheckFilterOptionsCapability( getCurentSfxFilter() ) )
+ sLabel += "...";
+
+ if ( sOldLabel != sLabel )
+ {
+ try
+ {
+ xCtrlAccess->setLabel( CommonFilePickerElementIds::PUSHBUTTON_OK, sLabel );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::updateExportButton" );
+ }
+ }
+}
+
+void FileDialogHelper_Impl::updateSelectionBox()
+{
+ if ( !mbHasSelectionBox )
+ return;
+
+ // Does the selection box exist?
+ bool bSelectionBoxFound = false;
+ uno::Reference< XControlInformation > xCtrlInfo( mxFileDlg, UNO_QUERY );
+ if ( xCtrlInfo.is() )
+ {
+ Sequence< OUString > aCtrlList = xCtrlInfo->getSupportedControls();
+ bSelectionBoxFound = comphelper::findValue(aCtrlList, "SelectionBox") != -1;
+ }
+
+ if ( bSelectionBoxFound )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = getCurentSfxFilter();
+ mbSelectionFltrEnabled = updateExtendedControl(
+ ExtendedFilePickerElementIds::CHECKBOX_SELECTION,
+ ( mbSelectionEnabled && pFilter && ( pFilter->GetFilterFlags() & SfxFilterFlags::SUPPORTSSELECTION ) ) );
+ uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+ xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0, Any( mbSelection ) );
+ }
+}
+
+void FileDialogHelper_Impl::enablePasswordBox( bool bInit )
+{
+ if ( ! mbHasPassword )
+ return;
+
+ bool bWasEnabled = mbIsPwdEnabled;
+
+ std::shared_ptr<const SfxFilter> pCurrentFilter = getCurentSfxFilter();
+ mbIsPwdEnabled = updateExtendedControl(
+ ExtendedFilePickerElementIds::CHECKBOX_PASSWORD,
+ pCurrentFilter && ( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::ENCRYPTION )
+ );
+
+ if( bInit )
+ {
+ // in case of initialization previous state is not interesting
+ if( mbIsPwdEnabled )
+ {
+ uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+ if( mbPwdCheckBoxState )
+ xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, Any( true ) );
+ }
+ }
+ else if( !bWasEnabled && mbIsPwdEnabled )
+ {
+ uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+ if( mbPwdCheckBoxState )
+ xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, Any( true ) );
+ }
+ else if( bWasEnabled && !mbIsPwdEnabled )
+ {
+ // remember user settings until checkbox is enabled
+ uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 );
+ bool bPassWord = false;
+ mbPwdCheckBoxState = ( aValue >>= bPassWord ) && bPassWord;
+ xCtrlAccess->setValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0, Any( false ) );
+ }
+}
+
+void FileDialogHelper_Impl::updatePreviewState( bool _bUpdatePreviewWindow )
+{
+ if ( !mbHasPreview )
+ return;
+
+ uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+
+ // check, whether or not we have to display a preview
+ if ( !xCtrlAccess.is() )
+ return;
+
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0 );
+ bool bShowPreview = false;
+
+ if ( aValue >>= bShowPreview )
+ {
+ mbShowPreview = bShowPreview;
+
+ // setShowState has currently no effect for the
+ // OpenOffice FilePicker (see svtools/source/filepicker/iodlg.cxx)
+ uno::Reference< XFilePreview > xFilePreview( mxFileDlg, UNO_QUERY );
+ if ( xFilePreview.is() )
+ xFilePreview->setShowState( mbShowPreview );
+
+ if ( _bUpdatePreviewWindow )
+ TimeOutHdl_Impl( nullptr );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::updatePreviewState" );
+ }
+}
+
+void FileDialogHelper_Impl::updateVersions()
+{
+ Sequence < OUString > aEntries;
+ Sequence < OUString > aPathSeq = mxFileDlg->getFiles();
+
+ if ( aPathSeq.getLength() == 1 )
+ {
+ INetURLObject aObj( aPathSeq[0] );
+
+ if ( ( aObj.GetProtocol() == INetProtocol::File ) &&
+ ( utl::UCBContentHelper::IsDocument( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) )
+ {
+ try
+ {
+ uno::Reference< embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
+ aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ embed::ElementModes::READ );
+
+ DBG_ASSERT( xStorage.is(), "The method must return the storage or throw exception!" );
+ if ( !xStorage.is() )
+ throw uno::RuntimeException();
+
+ const uno::Sequence < util::RevisionTag > xVersions = SfxMedium::GetVersionList( xStorage );
+
+ aEntries.realloc( xVersions.getLength() + 1 );
+ aEntries.getArray()[0] = SfxResId( STR_SFX_FILEDLG_ACTUALVERSION );
+
+ std::transform(xVersions.begin(), xVersions.end(), std::next(aEntries.getArray()),
+ [](const util::RevisionTag& rVersion) -> OUString { return rVersion.Identifier; });
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+
+ uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY );
+ Any aValue;
+
+ try
+ {
+ xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
+ ControlActions::DELETE_ITEMS, aValue );
+ }
+ catch( const IllegalArgumentException& ){}
+
+ if ( !aEntries.hasElements() )
+ return;
+
+ try
+ {
+ aValue <<= aEntries;
+ xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
+ ControlActions::ADD_ITEMS, aValue );
+
+ Any aPos;
+ aPos <<= sal_Int32(0);
+ xDlg->setValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
+ ControlActions::SET_SELECT_ITEM, aPos );
+ }
+ catch( const IllegalArgumentException& ){}
+}
+
+IMPL_LINK_NOARG(FileDialogHelper_Impl, TimeOutHdl_Impl, Timer *, void)
+{
+ if ( !mbHasPreview )
+ return;
+
+ maGraphic.Clear();
+
+ Any aAny;
+ uno::Reference < XFilePreview > xFilePicker( mxFileDlg, UNO_QUERY );
+
+ if ( ! xFilePicker.is() )
+ return;
+
+ Sequence < OUString > aPathSeq = mxFileDlg->getFiles();
+
+ if ( mbShowPreview && ( aPathSeq.getLength() == 1 ) )
+ {
+ OUString aURL = aPathSeq[0];
+
+ if ( ERRCODE_NONE == getGraphic( aURL, maGraphic ) )
+ {
+ // changed the code slightly;
+ // before: the bitmap was scaled and
+ // surrounded a white frame
+ // now: the bitmap will only be scaled
+ // and the filepicker implementation
+ // is responsible for placing it at its
+ // proper position and painting a frame
+
+ BitmapEx aBmp = maGraphic.GetBitmapEx();
+ if ( !aBmp.IsEmpty() )
+ {
+ // scale the bitmap to the correct size
+ sal_Int32 nOutWidth = xFilePicker->getAvailableWidth();
+ sal_Int32 nOutHeight = xFilePicker->getAvailableHeight();
+ sal_Int32 nBmpWidth = aBmp.GetSizePixel().Width();
+ sal_Int32 nBmpHeight = aBmp.GetSizePixel().Height();
+
+ double nXRatio = static_cast<double>(nOutWidth) / nBmpWidth;
+ double nYRatio = static_cast<double>(nOutHeight) / nBmpHeight;
+
+ if ( nXRatio < nYRatio )
+ aBmp.Scale( nXRatio, nXRatio );
+ else
+ aBmp.Scale( nYRatio, nYRatio );
+
+ // Convert to true color, to allow CopyPixel
+ aBmp.Convert( BmpConversion::N24Bit );
+
+ // and copy it into the Any
+ SvMemoryStream aData;
+
+ WriteDIB(aBmp, aData, false);
+
+ const Sequence < sal_Int8 > aBuffer(
+ static_cast< const sal_Int8* >(aData.GetData()),
+ aData.GetEndOfData() );
+
+ aAny <<= aBuffer;
+ }
+ }
+ }
+
+ try
+ {
+ SolarMutexReleaser aReleaseForCallback;
+ // clear the preview window
+ xFilePicker->setImage( FilePreviewImageFormats::BITMAP, aAny );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ }
+}
+
+ErrCode FileDialogHelper_Impl::getGraphic( const OUString& rURL,
+ Graphic& rGraphic ) const
+{
+ if ( utl::UCBContentHelper::IsFolder( rURL ) )
+ return ERRCODE_IO_NOTAFILE;
+
+ if ( !mpGraphicFilter )
+ return ERRCODE_IO_NOTSUPPORTED;
+
+ // select graphic filter from dialog filter selection
+ OUString aCurFilter( getFilter() );
+
+ sal_uInt16 nFilter = !aCurFilter.isEmpty() && mpGraphicFilter->GetImportFormatCount()
+ ? mpGraphicFilter->GetImportFormatNumber( aCurFilter )
+ : GRFILTER_FORMAT_DONTKNOW;
+
+ INetURLObject aURLObj( rURL );
+
+ if ( aURLObj.HasError() || INetProtocol::NotValid == aURLObj.GetProtocol() )
+ {
+ aURLObj.SetSmartProtocol( INetProtocol::File );
+ aURLObj.SetSmartURL( rURL );
+ }
+
+ ErrCode nRet = ERRCODE_NONE;
+
+ GraphicFilterImportFlags nFilterImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
+ // non-local?
+ if ( INetProtocol::File != aURLObj.GetProtocol() )
+ {
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( rURL, StreamMode::READ );
+
+ if( pStream )
+ nRet = mpGraphicFilter->ImportGraphic( rGraphic, rURL, *pStream, nFilter, nullptr, nFilterImportFlags );
+ else
+ nRet = mpGraphicFilter->ImportGraphic( rGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags );
+ }
+ else
+ {
+ nRet = mpGraphicFilter->ImportGraphic( rGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags );
+ }
+
+ return nRet;
+}
+
+ErrCode FileDialogHelper_Impl::getGraphic( Graphic& rGraphic ) const
+{
+ ErrCode nRet = ERRCODE_NONE;
+
+ // rhbz#1079672 do not return maGraphic, it needs not to be the selected file
+
+ OUString aPath;
+ Sequence<OUString> aPathSeq = mxFileDlg->getFiles();
+
+ if (aPathSeq.getLength() == 1)
+ {
+ aPath = aPathSeq[0];
+ }
+
+ if (!aPath.isEmpty())
+ nRet = getGraphic(aPath, rGraphic);
+ else
+ nRet = ERRCODE_IO_GENERAL;
+
+ return nRet;
+}
+
+static bool lcl_isSystemFilePicker( const uno::Reference< XExecutableDialog >& _rxFP )
+{
+ try
+ {
+ uno::Reference< XServiceInfo > xSI( _rxFP, UNO_QUERY );
+ if ( !xSI.is() )
+ return true;
+ return xSI->supportsService( "com.sun.star.ui.dialogs.SystemFilePicker" );
+ }
+ catch( const Exception& )
+ {
+ }
+ return false;
+}
+
+namespace {
+
+bool lcl_isAsyncFilePicker( const uno::Reference< XExecutableDialog >& _rxFP )
+{
+ try
+ {
+ uno::Reference<XAsynchronousExecutableDialog> xSI(_rxFP, UNO_QUERY);
+ return xSI.is();
+ }
+ catch( const Exception& )
+ {
+ }
+ return false;
+}
+
+enum open_or_save_t {OPEN, SAVE, UNDEFINED};
+
+}
+
+static open_or_save_t lcl_OpenOrSave(sal_Int16 const nDialogType)
+{
+ switch (nDialogType)
+ {
+ case FILEOPEN_SIMPLE:
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
+ case FILEOPEN_PLAY:
+ case FILEOPEN_LINK_PLAY:
+ case FILEOPEN_READONLY_VERSION:
+ case FILEOPEN_LINK_PREVIEW:
+ case FILEOPEN_PREVIEW:
+ return OPEN;
+ case FILESAVE_SIMPLE:
+ case FILESAVE_AUTOEXTENSION_PASSWORD:
+ case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
+ case FILESAVE_AUTOEXTENSION_SELECTION:
+ case FILESAVE_AUTOEXTENSION_TEMPLATE:
+ case FILESAVE_AUTOEXTENSION:
+ return SAVE;
+ default:
+ assert(false); // invalid dialog type
+ }
+ return UNDEFINED;
+}
+
+// FileDialogHelper_Impl
+
+css::uno::Reference<css::awt::XWindow> FileDialogHelper_Impl::GetFrameInterface()
+{
+ if (mpFrameWeld)
+ return mpFrameWeld->GetXWindow();
+ return css::uno::Reference<css::awt::XWindow>();
+}
+
+FileDialogHelper_Impl::FileDialogHelper_Impl(
+ FileDialogHelper* _pAntiImpl,
+ sal_Int16 nDialogType,
+ FileDialogFlags nFlags,
+ sal_Int16 nDialog,
+ weld::Window* pFrameWeld,
+ const OUString& sStandardDir,
+ const css::uno::Sequence< OUString >& rDenyList
+ )
+ :maPreviewIdle("sfx2 FileDialogHelper_Impl maPreviewIdle")
+ ,m_nDialogType ( nDialogType )
+ ,meContext ( FileDialogHelper::UnknownContext )
+{
+ const char* pServiceName=nullptr;
+ switch (nDialog)
+ {
+ case SFX2_IMPL_DIALOG_SYSTEM:
+ case SFX2_IMPL_DIALOG_OOO:
+ pServiceName = "com.sun.star.ui.dialogs.OfficeFilePicker";
+ break;
+ case SFX2_IMPL_DIALOG_REMOTE:
+ pServiceName = "com.sun.star.ui.dialogs.RemoteFilePicker";
+ break;
+ default:
+ pServiceName = "com.sun.star.ui.dialogs.FilePicker";
+ break;
+ }
+
+ OUString aService = OUString::createFromAscii( pServiceName );
+
+ uno::Reference< XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
+
+ // create the file open dialog
+ // the flags can be SFXWB_INSERT or SFXWB_MULTISELECTION
+
+ mpFrameWeld = pFrameWeld;
+ mpAntiImpl = _pAntiImpl;
+ mbHasAutoExt = false;
+ mbHasPassword = false;
+ m_bHaveFilterOptions = false;
+ mbIsPwdEnabled = true;
+ mbHasVersions = false;
+ mbHasPreview = false;
+ mbShowPreview = false;
+ mbDeleteMatcher = false;
+ mbInsert = bool(nFlags & (FileDialogFlags::Insert|
+ FileDialogFlags::InsertCompare|
+ FileDialogFlags::InsertMerge));
+ mbExport = bool(nFlags & FileDialogFlags::Export);
+ mbIsSaveDlg = false;
+ mbPwdCheckBoxState = false;
+ mbSelection = false;
+ mbSelectionEnabled = true;
+ mbHasSelectionBox = false;
+ mbSelectionFltrEnabled = false;
+
+ // default settings
+ m_nDontFlags = SFX_FILTER_NOTINSTALLED | SfxFilterFlags::INTERNAL | SfxFilterFlags::NOTINFILEDLG;
+ if (OPEN == lcl_OpenOrSave(m_nDialogType))
+ m_nMustFlags = SfxFilterFlags::IMPORT;
+ else
+ m_nMustFlags = SfxFilterFlags::EXPORT;
+
+
+ mpMatcher = nullptr;
+ mpGraphicFilter = nullptr;
+ mnPostUserEventId = nullptr;
+
+ // create the picker component
+ mxFileDlg.set(xFactory->createInstance( aService ), css::uno::UNO_QUERY);
+ mbSystemPicker = lcl_isSystemFilePicker( mxFileDlg );
+ mbAsyncPicker = lcl_isAsyncFilePicker(mxFileDlg);
+
+ uno::Reference< XInitialization > xInit( mxFileDlg, UNO_QUERY );
+
+ if ( ! mxFileDlg.is() )
+ {
+ return;
+ }
+
+
+ if ( xInit.is() )
+ {
+ sal_Int16 nTemplateDescription = TemplateDescription::FILEOPEN_SIMPLE;
+
+ switch ( m_nDialogType )
+ {
+ case FILEOPEN_SIMPLE:
+ nTemplateDescription = TemplateDescription::FILEOPEN_SIMPLE;
+ break;
+
+ case FILESAVE_SIMPLE:
+ nTemplateDescription = TemplateDescription::FILESAVE_SIMPLE;
+ mbIsSaveDlg = true;
+ break;
+
+ case FILESAVE_AUTOEXTENSION_PASSWORD:
+ nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD;
+ mbHasPassword = true;
+ mbHasAutoExt = true;
+ mbIsSaveDlg = true;
+ break;
+
+ case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
+ nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS;
+ mbHasPassword = true;
+
+ m_bHaveFilterOptions = true;
+ if( xFactory.is() )
+ {
+ mxFilterCFG.set(
+ xFactory->createInstance( "com.sun.star.document.FilterFactory" ),
+ UNO_QUERY );
+ }
+
+ mbHasAutoExt = true;
+ mbIsSaveDlg = true;
+ break;
+
+ case FILESAVE_AUTOEXTENSION_SELECTION:
+ nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_SELECTION;
+ mbHasAutoExt = true;
+ mbIsSaveDlg = true;
+ mbHasSelectionBox = true;
+ if ( mbExport && !mxFilterCFG.is() && xFactory.is() )
+ {
+ mxFilterCFG.set(
+ xFactory->createInstance( "com.sun.star.document.FilterFactory" ),
+ UNO_QUERY );
+ }
+ break;
+
+ case FILESAVE_AUTOEXTENSION_TEMPLATE:
+ nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION_TEMPLATE;
+ mbHasAutoExt = true;
+ mbIsSaveDlg = true;
+ break;
+
+ case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
+ nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE;
+ mbHasPreview = true;
+ break;
+
+ case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
+ nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR;
+ mbHasPreview = true;
+ break;
+
+ case FILEOPEN_PLAY:
+ nTemplateDescription = TemplateDescription::FILEOPEN_PLAY;
+ break;
+
+ case FILEOPEN_LINK_PLAY:
+ nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PLAY;
+ break;
+
+ case FILEOPEN_READONLY_VERSION:
+ nTemplateDescription = TemplateDescription::FILEOPEN_READONLY_VERSION;
+ mbHasVersions = true;
+ break;
+
+ case FILEOPEN_LINK_PREVIEW:
+ nTemplateDescription = TemplateDescription::FILEOPEN_LINK_PREVIEW;
+ mbHasPreview = true;
+ break;
+
+ case FILESAVE_AUTOEXTENSION:
+ nTemplateDescription = TemplateDescription::FILESAVE_AUTOEXTENSION;
+ mbHasAutoExt = true;
+ mbIsSaveDlg = true;
+ break;
+
+ case FILEOPEN_PREVIEW:
+ nTemplateDescription = TemplateDescription::FILEOPEN_PREVIEW;
+ mbHasPreview = true;
+ break;
+
+ default:
+ SAL_WARN( "sfx.dialog", "FileDialogHelper::ctor with unknown type" );
+ break;
+ }
+
+ if (mbHasPreview)
+ {
+ maPreviewIdle.SetPriority( TaskPriority::LOWEST );
+ maPreviewIdle.SetInvokeHandler( LINK( this, FileDialogHelper_Impl, TimeOutHdl_Impl ) );
+ }
+
+ auto xWindow = GetFrameInterface();
+
+ Sequence < Any > aInitArguments(!xWindow.is() ? 3 : 4);
+ auto pInitArguments = aInitArguments.getArray();
+
+ // This is a hack. We currently know that the internal file picker implementation
+ // supports the extended arguments as specified below.
+ // TODO:
+ // a) adjust the service description so that it includes the TemplateDescription and ParentWindow args
+ // b) adjust the implementation of the system file picker to that it recognizes it
+ if ( mbSystemPicker )
+ {
+ pInitArguments[0] <<= nTemplateDescription;
+ if (xWindow.is())
+ pInitArguments[1] <<= xWindow;
+ }
+ else
+ {
+ pInitArguments[0] <<= NamedValue(
+ "TemplateDescription",
+ Any( nTemplateDescription )
+ );
+
+ pInitArguments[1] <<= NamedValue(
+ "StandardDir",
+ Any( sStandardDir )
+ );
+
+ pInitArguments[2] <<= NamedValue(
+ "DenyList",
+ Any( rDenyList )
+ );
+
+
+ if (xWindow.is())
+ pInitArguments[3] <<= NamedValue("ParentWindow", Any(xWindow));
+ }
+
+ try
+ {
+ xInit->initialize( aInitArguments );
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "FileDialogHelper_Impl::FileDialogHelper_Impl: could not initialize the picker!" );
+ }
+ }
+
+
+ // set multiselection mode
+ if ( nFlags & FileDialogFlags::MultiSelection )
+ mxFileDlg->setMultiSelectionMode( true );
+
+ if ( nFlags & FileDialogFlags::Graphic ) // generate graphic filter only on demand
+ {
+ addGraphicFilter();
+ }
+
+ // Export dialog
+ if ( mbExport )
+ {
+ mxFileDlg->setTitle( SfxResId( STR_SFX_EXPLORERFILE_EXPORT ) );
+ try {
+ css::uno::Reference < XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY_THROW );
+ xCtrlAccess->enableControl( ExtendedFilePickerElementIds::LISTBOX_FILTER_SELECTOR, true );
+ }
+ catch( const Exception & ) { }
+ }
+
+ // Save a copy dialog
+ if ( nFlags & FileDialogFlags::SaveACopy )
+ {
+ mxFileDlg->setTitle( SfxResId( STR_PB_SAVEACOPY ) );
+ }
+
+ // the "insert file" dialog needs another title
+ if ( mbInsert )
+ {
+ if ( nFlags & FileDialogFlags::InsertCompare )
+ {
+ mxFileDlg->setTitle( SfxResId( STR_PB_COMPAREDOC ) );
+ }
+ else if ( nFlags & FileDialogFlags::InsertMerge )
+ {
+ mxFileDlg->setTitle( SfxResId( STR_PB_MERGEDOC ) );
+ }
+ else
+ {
+ mxFileDlg->setTitle( SfxResId( STR_SFX_EXPLORERFILE_INSERT ) );
+ }
+ uno::Reference < XFilePickerControlAccess > xExtDlg( mxFileDlg, UNO_QUERY );
+ if ( xExtDlg.is() )
+ {
+ try
+ {
+ xExtDlg->setLabel( CommonFilePickerElementIds::PUSHBUTTON_OK,
+ SfxResId( STR_SFX_EXPLORERFILE_BUTTONINSERT ) );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+ }
+
+ // add the event listener
+ mxFileDlg->addFilePickerListener( this );
+}
+
+css::uno::Reference<css::ui::dialogs::XFolderPicker2> createFolderPicker(const css::uno::Reference<css::uno::XComponentContext>& rContext, weld::Window* pPreferredParent)
+{
+ auto xRet = css::ui::dialogs::FolderPicker::create(rContext);
+
+ // see FileDialogHelper_Impl::FileDialogHelper_Impl (above) for args to FilePicker
+ // reuse the same arguments for FolderPicker
+ if (pPreferredParent && lcl_isSystemFilePicker(xRet))
+ {
+ uno::Reference< XInitialization > xInit(xRet, UNO_QUERY);
+ if (xInit.is())
+ {
+ Sequence<Any> aInitArguments{ Any(sal_Int32(0)), Any(pPreferredParent->GetXWindow()) };
+
+ try
+ {
+ xInit->initialize(aInitArguments);
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL( "createFolderPicker: could not initialize the picker!" );
+ }
+ }
+ }
+
+ return xRet;
+}
+
+FileDialogHelper_Impl::~FileDialogHelper_Impl()
+{
+ // Remove user event if we haven't received it yet
+ if ( mnPostUserEventId )
+ Application::RemoveUserEvent( mnPostUserEventId );
+ mnPostUserEventId = nullptr;
+
+ mpGraphicFilter.reset();
+
+ if ( mbDeleteMatcher )
+ delete mpMatcher;
+
+ maPreviewIdle.ClearInvokeHandler();
+
+ ::comphelper::disposeComponent( mxFileDlg );
+}
+
+void FileDialogHelper_Impl::setControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId )
+{
+ DBG_ASSERT( _pControlId && _pHelpId, "FileDialogHelper_Impl::setControlHelpIds: invalid array pointers!" );
+ if ( !_pControlId || !_pHelpId )
+ return;
+
+ // forward these ids to the file picker
+ try
+ {
+ const OUString sHelpIdPrefix( INET_HID_SCHEME );
+ // the ids for the single controls
+ uno::Reference< XFilePickerControlAccess > xControlAccess( mxFileDlg, UNO_QUERY );
+ if ( xControlAccess.is() )
+ {
+ while ( *_pControlId )
+ {
+ DBG_ASSERT( INetURLObject( OStringToOUString( *_pHelpId, RTL_TEXTENCODING_UTF8 ) ).GetProtocol() == INetProtocol::NotValid, "Wrong HelpId!" );
+ OUString sId = sHelpIdPrefix +
+ OUString( *_pHelpId, strlen( *_pHelpId ), RTL_TEXTENCODING_UTF8 );
+ xControlAccess->setValue( *_pControlId, ControlActions::SET_HELP_URL, Any( sId ) );
+
+ ++_pControlId; ++_pHelpId;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::setControlHelpIds: caught an exception while setting the help ids!" );
+ }
+}
+
+IMPL_LINK_NOARG( FileDialogHelper_Impl, InitControls, void*, void )
+{
+ mnPostUserEventId = nullptr;
+ enablePasswordBox( true );
+ updateFilterOptionsBox( );
+ updateSelectionBox( );
+}
+
+void FileDialogHelper_Impl::preExecute()
+{
+ loadConfig( );
+ setDefaultValues( );
+ updatePreviewState( false );
+
+ implInitializeFileName( );
+
+#if !(defined(MACOSX) && defined(MACOSX)) && !defined(_WIN32)
+ // allow for dialog implementations which need to be executed before they return valid values for
+ // current filter and such
+
+ // On Vista (at least SP1) it's the same as on MacOSX, the modal dialog won't let message pass
+ // through before it returns from execution
+ mnPostUserEventId = Application::PostUserEvent( LINK( this, FileDialogHelper_Impl, InitControls ) );
+#else
+ // However, the macOS implementation's pickers run modally in execute and so the event doesn't
+ // get through in time... so we call the methods directly
+ enablePasswordBox( true );
+ updateFilterOptionsBox( );
+ updateSelectionBox( );
+#endif
+}
+
+void FileDialogHelper_Impl::postExecute( sal_Int16 _nResult )
+{
+ if ( ExecutableDialogResults::CANCEL != _nResult )
+ saveConfig();
+}
+
+void FileDialogHelper_Impl::implInitializeFileName( )
+{
+ if ( maFileName.isEmpty() )
+ return;
+
+ INetURLObject aObj( maPath );
+ aObj.Append( maFileName );
+
+ // in case we're operating as save dialog, and "auto extension" is checked,
+ // cut the extension from the name
+ if ( !(mbIsSaveDlg && mbHasAutoExt) )
+ return;
+
+ try
+ {
+ bool bAutoExtChecked = false;
+
+ uno::Reference < XFilePickerControlAccess > xControlAccess( mxFileDlg, UNO_QUERY );
+ if ( xControlAccess.is()
+ && ( xControlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0 )
+ >>= bAutoExtChecked
+ )
+ )
+ {
+ if ( bAutoExtChecked )
+ { // cut the extension
+ aObj.removeExtension( );
+ mxFileDlg->setDefaultName(
+ aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "FileDialogHelper_Impl::implInitializeFileName: could not ask for the auto-extension current-value!" );
+ }
+}
+
+sal_Int16 FileDialogHelper_Impl::implDoExecute()
+{
+ preExecute();
+
+ sal_Int16 nRet = ExecutableDialogResults::CANCEL;
+
+//On MacOSX the native file picker has to run in the primordial thread because of drawing issues
+//On Linux the native gtk file picker, when backed by gnome-vfs2, needs to be run in the same
+//primordial thread as the ucb gnome-vfs2 provider was initialized in.
+
+ {
+ try
+ {
+#ifdef _WIN32
+ if ( mbSystemPicker )
+ {
+ SolarMutexReleaser aSolarMutex;
+ nRet = mxFileDlg->execute();
+ }
+ else
+#endif
+ nRet = mxFileDlg->execute();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::implDoExecute" );
+ }
+ }
+
+ postExecute( nRet );
+
+ return nRet;
+}
+
+void FileDialogHelper_Impl::implStartExecute()
+{
+ DBG_ASSERT( mxFileDlg.is(), "invalid file dialog" );
+
+ assert(mbAsyncPicker);
+ preExecute();
+
+ try
+ {
+ uno::Reference< XAsynchronousExecutableDialog > xAsyncDlg( mxFileDlg, UNO_QUERY );
+ if ( xAsyncDlg.is() )
+ xAsyncDlg->startExecuteModal( this );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::implDoExecute" );
+ }
+}
+
+void FileDialogHelper_Impl::implGetAndCacheFiles(const uno::Reference< XInterface >& xPicker, std::vector<OUString>& rpURLList)
+{
+ rpURLList.clear();
+
+ // a) the new way (optional!)
+ uno::Reference< XFilePicker3 > xPickNew(xPicker, UNO_QUERY);
+ if (xPickNew.is())
+ {
+ Sequence< OUString > lFiles = xPickNew->getSelectedFiles();
+ comphelper::sequenceToContainer(rpURLList, lFiles);
+ }
+
+ // b) the olde way ... non optional.
+ else
+ {
+ uno::Reference< XFilePicker3 > xPickOld(xPicker, UNO_QUERY_THROW);
+ Sequence< OUString > lFiles = xPickOld->getFiles();
+ ::sal_Int32 nFiles = lFiles.getLength();
+ if ( nFiles == 1 )
+ {
+ rpURLList.push_back(lFiles[0]);
+ }
+ else if ( nFiles > 1 )
+ {
+ INetURLObject aPath( lFiles[0] );
+ aPath.setFinalSlash();
+
+ for (::sal_Int32 i = 1; i < nFiles; i++)
+ {
+ if (i == 1)
+ aPath.Append( lFiles[i] );
+ else
+ aPath.setName( lFiles[i] );
+
+ rpURLList.push_back(aPath.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ }
+ }
+ }
+
+ mlLastURLs = rpURLList;
+}
+
+ErrCode FileDialogHelper_Impl::execute( std::vector<OUString>& rpURLList,
+ std::optional<SfxAllItemSet>& rpSet,
+ OUString& rFilter )
+{
+ // rFilter is a pure output parameter, it shouldn't be used for anything else
+ // changing this would surely break code
+ // rpSet is in/out parameter, usually just a media-descriptor that can be changed by dialog
+
+ uno::Reference< XFilePickerControlAccess > xCtrlAccess( mxFileDlg, UNO_QUERY );
+
+ // retrieves parameters from rpSet
+ // for now only Password is used
+ if ( rpSet )
+ {
+ // check password checkbox if the document had password before
+ if( mbHasPassword )
+ {
+ const SfxBoolItem* pPassItem = SfxItemSet::GetItem<SfxBoolItem>(&*rpSet, SID_PASSWORDINTERACTION, false);
+ mbPwdCheckBoxState = ( pPassItem != nullptr && pPassItem->GetValue() );
+
+ // in case the document has password to modify, the dialog should be shown
+ const SfxUnoAnyItem* pPassToModifyItem = SfxItemSet::GetItem<SfxUnoAnyItem>(&*rpSet, SID_MODIFYPASSWORDINFO, false);
+ mbPwdCheckBoxState |= ( pPassToModifyItem && pPassToModifyItem->GetValue().hasValue() );
+ }
+
+ const SfxBoolItem* pSelectItem = SfxItemSet::GetItem<SfxBoolItem>(&*rpSet, SID_SELECTION, false);
+ if ( pSelectItem )
+ mbSelection = pSelectItem->GetValue();
+ else
+ mbSelectionEnabled = false;
+
+ // the password will be set in case user decide so
+ rpSet->ClearItem( SID_PASSWORDINTERACTION );
+ if (rpSet->HasItem( SID_PASSWORD ))
+ {
+ // As the SID_ENCRYPTIONDATA and SID_PASSWORD are using for setting password together, we need to clear them both.
+ // Note: Do not remove SID_ENCRYPTIONDATA without SID_PASSWORD
+ rpSet->ClearItem( SID_PASSWORD );
+ rpSet->ClearItem( SID_ENCRYPTIONDATA );
+ }
+ rpSet->ClearItem( SID_RECOMMENDREADONLY );
+ rpSet->ClearItem( SID_MODIFYPASSWORDINFO );
+
+ }
+
+ if ( mbHasPassword && !mbPwdCheckBoxState )
+ {
+ mbPwdCheckBoxState = (
+ SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::DocWarnRecommendPassword ) );
+ }
+
+ rpURLList.clear();
+
+ if ( ! mxFileDlg.is() )
+ return ERRCODE_ABORT;
+
+ if ( ExecutableDialogResults::CANCEL != implDoExecute() )
+ {
+ // create an itemset if there is no
+ if( !rpSet )
+ rpSet.emplace( SfxGetpApp()->GetPool() );
+
+ // the item should remain only if it was set by the dialog
+ rpSet->ClearItem( SID_SELECTION );
+
+ if( mbExport && mbHasSelectionBox )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 );
+ bool bSelection = false;
+ if ( aValue >>= bSelection )
+ rpSet->Put( SfxBoolItem( SID_SELECTION, bSelection ) );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" );
+ }
+ }
+
+
+ // set the read-only flag. When inserting a file, this flag is always set
+ if ( mbInsert )
+ rpSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+ else
+ {
+ if ( ( FILEOPEN_READONLY_VERSION == m_nDialogType ) && xCtrlAccess.is() )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_READONLY, 0 );
+ bool bReadOnly = false;
+ if ( ( aValue >>= bReadOnly ) && bReadOnly )
+ rpSet->Put( SfxBoolItem( SID_DOC_READONLY, bReadOnly ) );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::execute: caught an IllegalArgumentException!" );
+ }
+ }
+ }
+ if ( mbHasVersions && xCtrlAccess.is() )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::LISTBOX_VERSION,
+ ControlActions::GET_SELECTED_ITEM_INDEX );
+ sal_Int32 nVersion = 0;
+ if ( ( aValue >>= nVersion ) && nVersion > 0 )
+ // open a special version; 0 == current version
+ rpSet->Put( SfxInt16Item( SID_VERSION, static_cast<short>(nVersion) ) );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ // set the filter
+ getRealFilter( rFilter );
+
+ std::shared_ptr<const SfxFilter> pCurrentFilter = getCurentSfxFilter();
+
+ // fill the rpURLList
+ implGetAndCacheFiles( mxFileDlg, rpURLList );
+ if ( rpURLList.empty() )
+ return ERRCODE_ABORT;
+
+ // check, whether or not we have to display a password box
+ if ( pCurrentFilter && mbHasPassword && mbIsPwdEnabled && xCtrlAccess.is() )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_PASSWORD, 0 );
+ bool bPassWord = false;
+ if ( ( aValue >>= bPassWord ) && bPassWord )
+ {
+ // ask for a password
+ OUString aDocName(rpURLList[0]);
+ ErrCode errCode = RequestPassword(pCurrentFilter, aDocName, &*rpSet, GetFrameInterface());
+ if (errCode != ERRCODE_NONE)
+ return errCode;
+ }
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+ // check, whether or not we have to display a key selection box
+ if ( pCurrentFilter && mbHasPassword && xCtrlAccess.is() )
+ {
+ try
+ {
+ Any aValue = xCtrlAccess->getValue( ExtendedFilePickerElementIds::CHECKBOX_GPGENCRYPTION, 0 );
+ bool bGpg = false;
+ if ( ( aValue >>= bGpg ) && bGpg )
+ {
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ while(true)
+ {
+ try
+ {
+ // ask for keys
+ aEncryptionData = ::comphelper::OStorageHelper::CreateGpgPackageEncryptionData();
+ break; // user cancelled or we've some keys now
+ }
+ catch( const IllegalArgumentException& )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(mpFrameWeld,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(RID_SVXSTR_GPG_ENCRYPT_FAILURE)));
+ xBox->run();
+ }
+ }
+
+ if ( aEncryptionData.hasElements() )
+ rpSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData) ) );
+ }
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ SaveLastUsedFilter();
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_ABORT;
+}
+
+ErrCode FileDialogHelper_Impl::execute()
+{
+ if ( ! mxFileDlg.is() )
+ return ERRCODE_ABORT;
+
+ sal_Int16 nRet = implDoExecute();
+
+ maPath = mxFileDlg->getDisplayDirectory();
+
+ if ( ExecutableDialogResults::CANCEL == nRet )
+ return ERRCODE_ABORT;
+ else
+ {
+ return ERRCODE_NONE;
+ }
+}
+
+OUString FileDialogHelper_Impl::getPath() const
+{
+ OUString aPath;
+
+ if ( mxFileDlg.is() )
+ aPath = mxFileDlg->getDisplayDirectory();
+
+ if ( aPath.isEmpty() )
+ aPath = maPath;
+
+ return aPath;
+}
+
+OUString FileDialogHelper_Impl::getFilter() const
+{
+ OUString aFilter = getCurrentFilterUIName();
+
+ if( aFilter.isEmpty() )
+ aFilter = maCurFilter;
+
+ return aFilter;
+}
+
+void FileDialogHelper_Impl::getRealFilter( OUString& _rFilter ) const
+{
+ _rFilter = getCurrentFilterUIName();
+
+ if ( _rFilter.isEmpty() )
+ _rFilter = maCurFilter;
+
+ if ( !_rFilter.isEmpty() && mpMatcher )
+ {
+ std::shared_ptr<const SfxFilter> pFilter =
+ mpMatcher->GetFilter4UIName( _rFilter, m_nMustFlags, m_nDontFlags );
+ _rFilter = pFilter ? pFilter->GetFilterName() : OUString();
+ }
+}
+
+void FileDialogHelper_Impl::verifyPath()
+{
+#ifdef UNX
+ // lp#905355, fdo#43895
+ // Check that the file has read only permission and is in /tmp -- this is
+ // the case if we have opened the file from the web with firefox only.
+ if (maFileName.isEmpty()) {
+ return;
+ }
+ INetURLObject url(maPath);
+ if (url.GetProtocol() != INetProtocol::File
+ || url.getName(0, true, INetURLObject::DecodeMechanism::WithCharset) != "tmp")
+ {
+ return;
+ }
+ if (maFileName.indexOf('/') != -1) {
+ SAL_WARN("sfx.dialog", maFileName << " contains /");
+ return;
+ }
+ url.insertName(
+ maFileName, false, INetURLObject::LAST_SEGMENT,
+ INetURLObject::EncodeMechanism::All);
+ OUString sysPathU;
+ osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL(
+ url.GetMainURL(INetURLObject::DecodeMechanism::NONE), sysPathU);
+ if (e != osl::FileBase::E_None) {
+ SAL_WARN(
+ "sfx.dialog",
+ "getSystemPathFromFileURL("
+ << url.GetMainURL(INetURLObject::DecodeMechanism::NONE) << ") failed with "
+ << +e);
+ return;
+ }
+ OString sysPathC;
+ if (!sysPathU.convertToString(
+ &sysPathC, osl_getThreadTextEncoding(),
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ SAL_WARN(
+ "sfx.dialog",
+ "convertToString(" << sysPathU << ") failed for encoding "
+ << +osl_getThreadTextEncoding());
+ return;
+ }
+ struct stat aFileStat;
+ if (stat(sysPathC.getStr(), &aFileStat) == -1) {
+ SAL_WARN( "sfx.dialog", "stat(" << sysPathC << ") failed with errno " << errno);
+ return;
+ }
+ if ((aFileStat.st_mode & (S_IRWXO | S_IRWXG | S_IRWXU)) == S_IRUSR) {
+ maPath = SvtPathOptions().GetWorkPath();
+ mxFileDlg->setDisplayDirectory( maPath );
+ }
+#else
+ (void) this;
+#endif
+}
+
+void FileDialogHelper_Impl::displayFolder( const OUString& _rPath )
+{
+ if ( _rPath.isEmpty() )
+ // nothing to do
+ return;
+
+ maPath = _rPath;
+ if ( mxFileDlg.is() )
+ {
+ try
+ {
+ mxFileDlg->setDisplayDirectory( maPath );
+ verifyPath();
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::displayFolder" );
+ }
+ }
+}
+
+void FileDialogHelper_Impl::setFileName( const OUString& _rFile )
+{
+ maFileName = _rFile;
+ if ( mxFileDlg.is() )
+ {
+ try
+ {
+ mxFileDlg->setDefaultName( maFileName );
+ verifyPath();
+ }
+ catch( const IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx", "FileDialogHelper_Impl::setFileName" );
+ }
+ }
+}
+
+void FileDialogHelper_Impl::setFilter( const OUString& rFilter )
+{
+ DBG_ASSERT( rFilter.indexOf(':') == -1, "Old filter name used!");
+
+ maCurFilter = rFilter;
+
+ if ( !rFilter.isEmpty() && mpMatcher )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = mpMatcher->GetFilter4FilterName(
+ rFilter, m_nMustFlags, m_nDontFlags );
+ if ( pFilter )
+ maCurFilter = pFilter->GetUIName();
+ }
+
+ if ( !maCurFilter.isEmpty() && mxFileDlg.is() )
+ {
+ try
+ {
+ mxFileDlg->setCurrentFilter( maCurFilter );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+}
+
+void FileDialogHelper_Impl::createMatcher( const OUString& rFactory )
+{
+ if (mbDeleteMatcher)
+ delete mpMatcher;
+
+ mpMatcher = new SfxFilterMatcher( SfxObjectShell::GetServiceNameFromFactory(rFactory) );
+ mbDeleteMatcher = true;
+}
+
+void FileDialogHelper_Impl::addFilters( const OUString& rFactory,
+ SfxFilterFlags nMust,
+ SfxFilterFlags nDont )
+{
+ if ( ! mxFileDlg.is() )
+ return;
+
+ if (mbDeleteMatcher)
+ delete mpMatcher;
+
+ // we still need a matcher to convert UI names to filter names
+ if ( rFactory.isEmpty() )
+ {
+ SfxApplication *pSfxApp = SfxGetpApp();
+ mpMatcher = &pSfxApp->GetFilterMatcher();
+ mbDeleteMatcher = false;
+ }
+ else
+ {
+ mpMatcher = new SfxFilterMatcher( rFactory );
+ mbDeleteMatcher = true;
+ }
+
+ uno::Reference< XMultiServiceFactory > xSMGR = ::comphelper::getProcessServiceFactory();
+ uno::Reference< XContainerQuery > xFilterCont(
+ xSMGR->createInstance("com.sun.star.document.FilterFactory"),
+ UNO_QUERY);
+ if ( ! xFilterCont.is() )
+ return;
+
+ m_nMustFlags |= nMust;
+ m_nDontFlags |= nDont;
+
+ // create the list of filters
+ OUString sQuery =
+ "getSortedFilterList()"
+ ":module=" +
+ rFactory + // use long name here !
+ ":iflags=" +
+ OUString::number(static_cast<sal_Int32>(m_nMustFlags)) +
+ ":eflags=" +
+ OUString::number(static_cast<sal_Int32>(m_nDontFlags));
+
+ uno::Reference< XEnumeration > xResult;
+ try
+ {
+ xResult = xFilterCont->createSubSetEnumerationByQuery(sQuery);
+ }
+ catch( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not get filters from the configuration!" );
+ }
+
+ TSortedFilterList aIter (xResult);
+
+ // append the filters
+ OUString sFirstFilter;
+ if (OPEN == lcl_OpenOrSave(m_nDialogType))
+ ::sfx2::appendFiltersForOpen( aIter, mxFileDlg, sFirstFilter, *this );
+ else if ( mbExport )
+ ::sfx2::appendExportFilters( aIter, mxFileDlg, sFirstFilter, *this );
+ else
+ ::sfx2::appendFiltersForSave( aIter, mxFileDlg, sFirstFilter, *this, rFactory );
+
+ // set our initial selected filter (if we do not already have one)
+ if ( maSelectFilter.isEmpty() )
+ maSelectFilter = sFirstFilter;
+}
+
+void FileDialogHelper_Impl::addFilter( const OUString& rFilterName,
+ const OUString& rExtension )
+{
+ if ( ! mxFileDlg.is() )
+ return;
+
+ try
+ {
+ mxFileDlg->appendFilter( rFilterName, rExtension );
+
+ if ( maSelectFilter.isEmpty() )
+ maSelectFilter = rFilterName;
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append Filter" << rFilterName );
+ }
+}
+
+void FileDialogHelper_Impl::addGraphicFilter()
+{
+ if ( ! mxFileDlg.is() )
+ return;
+
+ // create the list of filters
+ mpGraphicFilter.reset( new GraphicFilter );
+ sal_uInt16 i, j, nCount = mpGraphicFilter->GetImportFormatCount();
+
+ // compute the extension string for all known import filters
+ OUString aExtensions;
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ j = 0;
+ while( true )
+ {
+ OUString sWildcard = mpGraphicFilter->GetImportWildcard( i, j++ );
+ if ( sWildcard.isEmpty() )
+ break;
+ if ( aExtensions.indexOf( sWildcard ) == -1 )
+ {
+ if ( !aExtensions.isEmpty() )
+ aExtensions += ";";
+ aExtensions += sWildcard;
+ }
+ }
+ }
+
+#if defined(_WIN32)
+ if ( aExtensions.getLength() > 240 )
+ aExtensions = FILEDIALOG_FILTER_ALL;
+#endif
+ bool bIsInOpenMode = isInOpenMode();
+
+ try
+ {
+ // if the extension is not "All files", insert "All images"
+ if (aExtensions != FILEDIALOG_FILTER_ALL)
+ {
+ OUString aAllFilterName = SfxResId(STR_SFX_IMPORT_ALL_IMAGES);
+ aAllFilterName = ::sfx2::addExtension( aAllFilterName, aExtensions, bIsInOpenMode, *this );
+ mxFileDlg->appendFilter( aAllFilterName, aExtensions );
+ maSelectFilter = aAllFilterName; // and make it the default
+ }
+
+ // rhbz#1715109 always include All files *.* or *
+ OUString aAllFilesName = SfxResId( STR_SFX_FILTERNAME_ALL );
+ aAllFilesName = ::sfx2::addExtension( aAllFilesName, FILEDIALOG_FILTER_ALL, bIsInOpenMode, *this );
+ mxFileDlg->appendFilter( aAllFilesName, FILEDIALOG_FILTER_ALL );
+
+ // if the extension is "All files", make that the default
+ if (aExtensions == FILEDIALOG_FILTER_ALL)
+ maSelectFilter = aAllFilesName;
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append Filter" );
+ }
+
+ // Now add the filter
+ for ( i = 0; i < nCount; i++ )
+ {
+ OUString aName = mpGraphicFilter->GetImportFormatName( i );
+ OUString aExt;
+ j = 0;
+ while( true )
+ {
+ OUString sWildcard = mpGraphicFilter->GetImportWildcard( i, j++ );
+ if ( sWildcard.isEmpty() )
+ break;
+ if ( aExt.indexOf( sWildcard ) == -1 )
+ {
+ if ( !aExt.isEmpty() )
+ aExt += ";";
+ aExt += sWildcard;
+ }
+ }
+ aName = ::sfx2::addExtension( aName, aExt, bIsInOpenMode, *this );
+ try
+ {
+ mxFileDlg->appendFilter( aName, aExt );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append Filter" );
+ }
+ }
+}
+
+constexpr OUStringLiteral GRF_CONFIG_STR = u" ";
+constexpr OUStringLiteral STD_CONFIG_STR = u"1 ";
+
+static void SetToken( OUString& rOrigStr, sal_Int32 nToken, sal_Unicode cTok, std::u16string_view rStr)
+{
+ const sal_Unicode* pStr = rOrigStr.getStr();
+ sal_Int32 nLen = rOrigStr.getLength();
+ sal_Int32 nTok = 0;
+ sal_Int32 nFirstChar = 0;
+ sal_Int32 i = nFirstChar;
+
+ // Determine token position and length
+ pStr += i;
+ while ( i < nLen )
+ {
+ // Increase token count if match
+ if ( *pStr == cTok )
+ {
+ ++nTok;
+
+ if ( nTok == nToken )
+ nFirstChar = i+1;
+ else
+ {
+ if ( nTok > nToken )
+ break;
+ }
+ }
+
+ ++pStr;
+ ++i;
+ }
+
+ if ( nTok >= nToken )
+ rOrigStr = rOrigStr.replaceAt( nFirstChar, i-nFirstChar, rStr );
+}
+
+namespace
+{
+void SaveLastDirectory(OUString const& sContext, OUString const& sDirectory)
+{
+ if (sContext.isEmpty())
+ return;
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(
+ comphelper::ConfigurationChanges::create());
+ Reference<container::XNameContainer> set(
+ officecfg::Office::Common::Misc::FilePickerLastDirectory::get(batch));
+
+ bool found;
+ Any v;
+ try
+ {
+ v = set->getByName(sContext);
+ found = true;
+ }
+ catch (container::NoSuchElementException&)
+ {
+ found = false;
+ }
+ if (found)
+ {
+ Reference<XPropertySet> el(v.get<Reference<XPropertySet>>(), UNO_SET_THROW);
+ el->setPropertyValue("LastPath", Any(sDirectory));
+ }
+ else
+ {
+ Reference<XPropertySet> el(
+ (Reference<lang::XSingleServiceFactory>(set, UNO_QUERY_THROW)->createInstance()),
+ UNO_QUERY_THROW);
+ el->setPropertyValue("LastPath", Any(sDirectory));
+ Any v2(el);
+ set->insertByName(sContext, v2);
+ }
+ batch->commit();
+}
+}
+
+void FileDialogHelper_Impl::saveConfig()
+{
+ uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY );
+ Any aValue;
+
+ if ( ! xDlg.is() )
+ return;
+
+ if ( mbHasPreview )
+ {
+ SvtViewOptions aDlgOpt( EViewType::Dialog, IMPGRF_CONFIGNAME );
+
+ try
+ {
+ aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0 );
+ bool bValue = false;
+ aValue >>= bValue;
+ OUString aUserData(GRF_CONFIG_STR);
+ SetToken( aUserData, 1, ' ', OUString::number( static_cast<sal_Int32>(bValue) ) );
+
+ INetURLObject aObj( getPath() );
+
+ if ( aObj.GetProtocol() == INetProtocol::File )
+ SetToken( aUserData, 2, ' ', aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ OUString aFilter = getFilter();
+ aFilter = EncodeSpaces_Impl( aFilter );
+ SetToken( aUserData, 3, ' ', aFilter );
+
+ aDlgOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+ else
+ {
+ bool bWriteConfig = false;
+ SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME );
+ OUString aUserData(STD_CONFIG_STR);
+
+ if ( aDlgOpt.Exists() )
+ {
+ Any aUserItem = aDlgOpt.GetUserItem( USERITEM_NAME );
+ OUString aTemp;
+ if ( aUserItem >>= aTemp )
+ aUserData = aTemp;
+ }
+
+ if ( mbHasAutoExt )
+ {
+ try
+ {
+ aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0 );
+ bool bAutoExt = true;
+ aValue >>= bAutoExt;
+ SetToken( aUserData, 0, ' ', OUString::number( static_cast<sal_Int32>(bAutoExt) ) );
+ bWriteConfig = true;
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ if ( ! mbIsSaveDlg )
+ {
+ OUString aPath = getPath();
+ if ( comphelper::isFileUrl( aPath ) )
+ {
+ SetToken( aUserData, 1, ' ', aPath );
+ bWriteConfig = true;
+ }
+ }
+
+ if( mbHasSelectionBox && mbSelectionFltrEnabled )
+ {
+ try
+ {
+ aValue = xDlg->getValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0 );
+ bool bSelection = true;
+ aValue >>= bSelection;
+ if ( comphelper::string::getTokenCount(aUserData, ' ') < 3 )
+ aUserData += " ";
+ SetToken( aUserData, 2, ' ', OUString::number( static_cast<sal_Int32>(bSelection) ) );
+ bWriteConfig = true;
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ if ( bWriteConfig )
+ aDlgOpt.SetUserItem( USERITEM_NAME, Any( aUserData ) );
+ }
+
+ // Store to config, if explicit context is set. Otherwise store in (global) runtime var.
+ if (meContext != FileDialogHelper::UnknownContext)
+ {
+ SaveLastDirectory(FileDialogHelper::contextToString(meContext), getPath());
+ }
+ else
+ {
+ SfxApplication *pSfxApp = SfxGetpApp();
+ pSfxApp->SetLastDir_Impl( getPath() );
+ }
+}
+
+OUString FileDialogHelper_Impl::getInitPath(std::u16string_view _rFallback,
+ const sal_Int32 _nFallbackToken)
+{
+ OUString sPath;
+ // Load from config, if explicit context is set. Otherwise load from (global) runtime var.
+ if (meContext != FileDialogHelper::UnknownContext)
+ {
+ OUString sContext = FileDialogHelper::contextToString(meContext);
+ Reference<XNameAccess> set(officecfg::Office::Common::Misc::FilePickerLastDirectory::get());
+ Any v;
+ try
+ {
+ v = set->getByName(sContext);
+ Reference<XPropertySet> el(v.get<Reference<XPropertySet>>(), UNO_SET_THROW);
+ sPath = el->getPropertyValue("LastPath").get<OUString>();
+ }
+ catch (NoSuchElementException&)
+ {
+ }
+ }
+ else
+ {
+ SfxApplication *pSfxApp = SfxGetpApp();
+ sPath = pSfxApp->GetLastDir_Impl();
+ }
+
+ if ( sPath.isEmpty() )
+ sPath = o3tl::getToken(_rFallback, _nFallbackToken, ' ' );
+
+ // check if the path points to a valid (accessible) directory
+ bool bValid = false;
+ if ( !sPath.isEmpty() )
+ {
+ OUString sPathCheck( sPath );
+ if ( sPathCheck[ sPathCheck.getLength() - 1 ] != '/' )
+ sPathCheck += "/";
+ sPathCheck += ".";
+ try
+ {
+ ::ucbhelper::Content aContent( sPathCheck,
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext() );
+ bValid = aContent.isFolder();
+ }
+ catch( const Exception& ) {}
+ }
+ if ( !bValid )
+ sPath.clear();
+ return sPath;
+}
+
+void FileDialogHelper_Impl::loadConfig()
+{
+ uno::Reference < XFilePickerControlAccess > xDlg( mxFileDlg, UNO_QUERY );
+ Any aValue;
+
+ if ( ! xDlg.is() )
+ return;
+
+ if ( mbHasPreview )
+ {
+ SvtViewOptions aViewOpt( EViewType::Dialog, IMPGRF_CONFIGNAME );
+ OUString aUserData;
+
+ if ( aViewOpt.Exists() )
+ {
+ Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
+ OUString aTemp;
+ if ( aUserItem >>= aTemp )
+ aUserData = aTemp;
+ }
+
+ if ( !aUserData.isEmpty() )
+ {
+ try
+ {
+ // respect the last "insert as link" state
+ bool bLink = o3tl::toInt32(o3tl::getToken(aUserData, 0, ' ' ));
+ aValue <<= bLink;
+ xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_LINK, 0, aValue );
+
+ // respect the last "show preview" state
+ bool bShowPreview = o3tl::toInt32(o3tl::getToken(aUserData, 1, ' ' ));
+ aValue <<= bShowPreview;
+ xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, aValue );
+
+ if ( maPath.isEmpty() )
+ displayFolder( getInitPath( aUserData, 2 ) );
+
+ if ( maCurFilter.isEmpty() )
+ {
+ OUString aFilter = aUserData.getToken( 3, ' ' );
+ aFilter = DecodeSpaces_Impl( aFilter );
+ setFilter( aFilter );
+ }
+
+ // set the member so we know that we have to show the preview
+ mbShowPreview = bShowPreview;
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ if ( maPath.isEmpty() )
+ displayFolder( SvtPathOptions().GetWorkPath() );
+ }
+ else
+ {
+ SvtViewOptions aViewOpt( EViewType::Dialog, IODLG_CONFIGNAME );
+ OUString aUserData;
+
+ if ( aViewOpt.Exists() )
+ {
+ Any aUserItem = aViewOpt.GetUserItem( USERITEM_NAME );
+ OUString aTemp;
+ if ( aUserItem >>= aTemp )
+ aUserData = aTemp;
+ }
+
+ if ( aUserData.isEmpty() )
+ aUserData = STD_CONFIG_STR;
+
+ if ( maPath.isEmpty() )
+ displayFolder( getInitPath( aUserData, 1 ) );
+
+ if ( mbHasAutoExt )
+ {
+ sal_Int32 nFlag = o3tl::toInt32(o3tl::getToken(aUserData, 0, ' ' ));
+ aValue <<= static_cast<bool>(nFlag);
+ try
+ {
+ xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, aValue );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ if( mbHasSelectionBox )
+ {
+ sal_Int32 nFlag = o3tl::toInt32(o3tl::getToken(aUserData, 2, ' ' ));
+ aValue <<= static_cast<bool>(nFlag);
+ try
+ {
+ xDlg->setValue( ExtendedFilePickerElementIds::CHECKBOX_SELECTION, 0, aValue );
+ }
+ catch( const IllegalArgumentException& ){}
+ }
+
+ if ( maPath.isEmpty() )
+ displayFolder( SvtPathOptions().GetWorkPath() );
+ }
+}
+
+void FileDialogHelper_Impl::setDefaultValues()
+{
+ // when no filter is set, we set the currentFilter to <all>
+ if ( maCurFilter.isEmpty() && !maSelectFilter.isEmpty() )
+ {
+ try
+ {
+ mxFileDlg->setCurrentFilter( maSelectFilter );
+ }
+ catch( const IllegalArgumentException& )
+ {}
+ }
+
+ // when no path is set, we use the standard 'work' folder
+ if ( maPath.isEmpty() )
+ {
+ OUString aWorkFolder = SvtPathOptions().GetWorkPath();
+ try
+ {
+ mxFileDlg->setDisplayDirectory( aWorkFolder );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx.dialog", "FileDialogHelper_Impl::setDefaultValues: caught an exception while setting the display directory!" );
+ }
+ }
+}
+
+bool FileDialogHelper_Impl::isShowFilterExtensionEnabled() const
+{
+ return !maFilters.empty();
+}
+
+void FileDialogHelper_Impl::addFilterPair( const OUString& rFilter,
+ const OUString& rFilterWithExtension )
+{
+ maFilters.emplace_back( rFilter, rFilterWithExtension );
+
+}
+
+OUString FileDialogHelper_Impl::getFilterName( std::u16string_view rFilterWithExtension ) const
+{
+ OUString sRet;
+ for (auto const& filter : maFilters)
+ {
+ if (filter.Second == rFilterWithExtension)
+ {
+ sRet = filter.First;
+ break;
+ }
+ }
+ return sRet;
+}
+
+OUString FileDialogHelper_Impl::getFilterWithExtension( std::u16string_view rFilter ) const
+{
+ OUString sRet;
+ for (auto const& filter : maFilters)
+ {
+ if ( filter.First == rFilter )
+ {
+ sRet = filter.Second;
+ break;
+ }
+ }
+ return sRet;
+}
+
+void FileDialogHelper_Impl::SetContext( FileDialogHelper::Context _eNewContext )
+{
+ meContext = _eNewContext;
+
+ std::optional<OUString> pConfigId = GetLastFilterConfigId( _eNewContext );
+ if( pConfigId )
+ LoadLastUsedFilter( *pConfigId );
+}
+
+// FileDialogHelper
+
+FileDialogHelper::FileDialogHelper(
+ sal_Int16 nDialogType,
+ FileDialogFlags nFlags,
+ const OUString& rFact,
+ SfxFilterFlags nMust,
+ SfxFilterFlags nDont,
+ weld::Window* pPreferredParent)
+ : m_nError(0),
+ mpImpl(new FileDialogHelper_Impl(this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent))
+{
+
+ // create the list of filters
+ mpImpl->addFilters(
+ SfxObjectShell::GetServiceNameFromFactory(rFact), nMust, nDont );
+}
+
+FileDialogHelper::FileDialogHelper(
+ sal_Int16 nDialogType,
+ FileDialogFlags nFlags,
+ const OUString& rFact,
+ sal_Int16 nDialog,
+ SfxFilterFlags nMust,
+ SfxFilterFlags nDont,
+ const OUString& rStandardDir,
+ const css::uno::Sequence< OUString >& rDenyList,
+ weld::Window* pPreferredParent)
+ : m_nError(0),
+ mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, nDialog, pPreferredParent, rStandardDir, rDenyList ) )
+{
+ // create the list of filters
+ mpImpl->addFilters(
+ SfxObjectShell::GetServiceNameFromFactory(rFact), nMust, nDont );
+}
+
+FileDialogHelper::FileDialogHelper(sal_Int16 nDialogType, FileDialogFlags nFlags, weld::Window* pPreferredParent)
+ : m_nError(0),
+ mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent ) )
+{
+}
+
+FileDialogHelper::FileDialogHelper(
+ sal_Int16 nDialogType,
+ FileDialogFlags nFlags,
+ const OUString& aFilterUIName,
+ std::u16string_view aExtName,
+ const OUString& rStandardDir,
+ const css::uno::Sequence< OUString >& rDenyList,
+ weld::Window* pPreferredParent )
+ : m_nError(0),
+ mpImpl( new FileDialogHelper_Impl( this, nDialogType, nFlags, SFX2_IMPL_DIALOG_CONFIG, pPreferredParent, rStandardDir, rDenyList ) )
+{
+ // the wildcard here is expected in form "*.extension"
+ OUString aWildcard;
+ if ( aExtName.find( '*' ) != 0 )
+ {
+ if ( !aExtName.empty() && aExtName.find( '.' ) != 0 )
+ aWildcard = "*.";
+ else
+ aWildcard = "*";
+ }
+
+ aWildcard += aExtName;
+
+ OUString const aUIString = ::sfx2::addExtension(
+ aFilterUIName, aWildcard, (OPEN == lcl_OpenOrSave(mpImpl->m_nDialogType)), *mpImpl);
+ AddFilter( aUIString, aWildcard );
+}
+
+FileDialogHelper::~FileDialogHelper()
+{
+ mpImpl->dispose();
+}
+
+void FileDialogHelper::CreateMatcher( const OUString& rFactory )
+{
+ mpImpl->createMatcher( SfxObjectShell::GetServiceNameFromFactory(rFactory) );
+}
+
+void FileDialogHelper::SetControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId )
+{
+ mpImpl->setControlHelpIds( _pControlId, _pHelpId );
+}
+
+void FileDialogHelper::SetContext( Context _eNewContext )
+{
+ mpImpl->SetContext( _eNewContext );
+}
+
+OUString FileDialogHelper::contextToString(Context context)
+{
+ // These strings are used in the configuration, to store the last used directory for each context.
+ // Please don't change them.
+ switch(context) {
+ case AcceleratorConfig:
+ return "AcceleratorConfig";
+ case AutoRedact:
+ return "AutoRedact";
+ case BaseDataSource:
+ return "BaseDataSource";
+ case BaseSaveAs:
+ return "BaseSaveAs";
+ case BasicExportDialog:
+ return "BasicExportDialog";
+ case BasicExportPackage:
+ return "BasicExportPackage";
+ case BasicExportSource:
+ return "BasicExportSource";
+ case BasicImportDialog:
+ return "BasicImportDialog";
+ case BasicImportSource:
+ return "BasicImportSource";
+ case BasicInsertLib:
+ return "BasicInsertLib";
+ case BulletsAddImage:
+ return "BulletsAddImage";
+ case CalcDataProvider:
+ return "CalcDataProvider";
+ case CalcDataStream:
+ return "CalcDataStream";
+ case CalcExport:
+ return "CalcExport";
+ case CalcSaveAs:
+ return "CalcSaveAs";
+ case CalcXMLSource:
+ return "CalcXMLSource";
+ case ExportImage:
+ return "ExportImage";
+ case ExtensionManager:
+ return "ExtensionManager";
+ case FormsAddInstance:
+ return "FormsAddInstance";
+ case FormsInsertImage:
+ return "FormsInsertImage";
+ case LinkClientOLE:
+ return "LinkClientOLE";
+ case LinkClientFile:
+ return "LinkClientFile";
+ case DrawImpressInsertFile:
+ return "DrawImpressInsertFile";
+ case DrawImpressOpenSound:
+ return "DrawImpressOpenSound";
+ case DrawExport:
+ return "DrawExport";
+ case DrawSaveAs:
+ return "DrawSaveAs";
+ case IconImport:
+ return "IconImport";
+ case ImpressClickAction:
+ return "ImpressClickAction";
+ case ImpressExport:
+ return "ImpressExport";
+ case ImpressPhotoDialog:
+ return "ImpressPhotoDialog";
+ case ImpressSaveAs:
+ return "ImpressSaveAs";
+ case ImageMap:
+ return "ImageMap";
+ case InsertDoc:
+ return "InsertDoc";
+ case InsertImage:
+ return "InsertImage";
+ case InsertOLE:
+ return "InsertOLE";
+ case InsertMedia:
+ return "InsertMedia";
+ case JavaClassPath:
+ return "JavaClassPath";
+ case ReportInsertImage:
+ return "ReportInsertImage";
+ case ScreenshotAnnotation:
+ return "ScreenshotAnnotation";
+ case SignatureLine:
+ return "SignatureLine";
+ case TemplateImport:
+ return "TemplateImport";
+ case WriterCreateAddressList:
+ return "WriterCreateAddressList";
+ case WriterExport:
+ return "WriterExport";
+ case WriterImportAutotext:
+ return "WriterImportAutotext";
+ case WriterInsertDoc:
+ return "WriterInsertDoc";
+ case WriterInsertHyperlink:
+ return "WriterInsertHyperlink";
+ case WriterInsertImage:
+ return "WriterInsertImage";
+ case WriterInsertScript:
+ return "WriterInsertScript";
+ case WriterLoadTemplate:
+ return "WriterLoadTemplate";
+ case WriterMailMerge:
+ return "WriterMailMerge";
+ case WriterMailMergeSaveAs:
+ return "WriterMailMergeSaveAs";
+ case WriterNewHTMLGlobalDoc:
+ return "WriterNewHTMLGlobalDoc";
+ case WriterRegisterDataSource:
+ return "WriterRegisterDataSource";
+ case WriterSaveAs:
+ return "WriterSaveAs";
+ case WriterSaveHTML:
+ return "WriterSaveHTML";
+ case XMLFilterSettings:
+ return "XMLFilterSettings";
+ case UnknownContext:
+ default:
+ return "";
+ }
+}
+
+IMPL_LINK_NOARG(FileDialogHelper, ExecuteSystemFilePicker, void*, void)
+{
+ m_nError = mpImpl->execute();
+ m_aDialogClosedLink.Call( this );
+}
+
+// rDirPath has to be a directory
+ErrCode FileDialogHelper::Execute( std::vector<OUString>& rpURLList,
+ std::optional<SfxAllItemSet>& rpSet,
+ OUString& rFilter,
+ const OUString& rDirPath )
+{
+ SetDisplayFolder( rDirPath );
+ return mpImpl->execute( rpURLList, rpSet, rFilter );
+}
+
+
+ErrCode FileDialogHelper::Execute()
+{
+ return mpImpl->execute();
+}
+
+ErrCode FileDialogHelper::Execute( std::optional<SfxAllItemSet>& rpSet,
+ OUString& rFilter )
+{
+ ErrCode nRet;
+ std::vector<OUString> rURLList;
+ nRet = mpImpl->execute(rURLList, rpSet, rFilter);
+ return nRet;
+}
+
+void FileDialogHelper::StartExecuteModal( const Link<FileDialogHelper*,void>& rEndDialogHdl )
+{
+ m_aDialogClosedLink = rEndDialogHdl;
+ m_nError = ERRCODE_NONE;
+ if (!mpImpl->isAsyncFilePicker())
+ Application::PostUserEvent( LINK( this, FileDialogHelper, ExecuteSystemFilePicker ) );
+ else
+ mpImpl->implStartExecute();
+}
+
+sal_Int16 FileDialogHelper::GetDialogType() const { return mpImpl ? mpImpl->m_nDialogType : 0; }
+
+bool FileDialogHelper::IsPasswordEnabled() const
+{
+ return mpImpl && mpImpl->isPasswordEnabled();
+}
+
+OUString FileDialogHelper::GetRealFilter() const
+{
+ OUString sFilter;
+ if (mpImpl)
+ mpImpl->getRealFilter( sFilter );
+ return sFilter;
+}
+
+void FileDialogHelper::SetTitle( const OUString& rNewTitle )
+{
+ if ( mpImpl->mxFileDlg.is() )
+ mpImpl->mxFileDlg->setTitle( rNewTitle );
+}
+
+OUString FileDialogHelper::GetPath() const
+{
+ OUString aPath;
+
+ if ( !mpImpl->mlLastURLs.empty())
+ return mpImpl->mlLastURLs[0];
+
+ if ( mpImpl->mxFileDlg.is() )
+ {
+ Sequence < OUString > aPathSeq = mpImpl->mxFileDlg->getFiles();
+
+ if ( aPathSeq.getLength() == 1 )
+ {
+ aPath = aPathSeq[0];
+ }
+ }
+
+ return aPath;
+}
+
+Sequence < OUString > FileDialogHelper::GetMPath() const
+{
+ if ( !mpImpl->mlLastURLs.empty())
+ return comphelper::containerToSequence(mpImpl->mlLastURLs);
+
+ if ( mpImpl->mxFileDlg.is() )
+ return mpImpl->mxFileDlg->getFiles();
+ else
+ {
+ Sequence < OUString > aEmpty;
+ return aEmpty;
+ }
+}
+
+Sequence< OUString > FileDialogHelper::GetSelectedFiles() const
+{
+ // a) the new way (optional!)
+ uno::Sequence< OUString > aResultSeq;
+ if (mpImpl->mxFileDlg.is())
+ {
+ aResultSeq = mpImpl->mxFileDlg->getSelectedFiles();
+ }
+ // b) the olde way ... non optional.
+ else
+ {
+ uno::Reference< XFilePicker > xPickOld(mpImpl->mxFileDlg, UNO_QUERY_THROW);
+ Sequence< OUString > lFiles = xPickOld->getFiles();
+ ::sal_Int32 nFiles = lFiles.getLength();
+ if ( nFiles > 1 )
+ {
+ aResultSeq = Sequence< OUString >( nFiles-1 );
+ auto pResultSeq = aResultSeq.getArray();
+
+ INetURLObject aPath( lFiles[0] );
+ aPath.setFinalSlash();
+
+ for (::sal_Int32 i = 1; i < nFiles; i++)
+ {
+ if (i == 1)
+ aPath.Append( lFiles[i] );
+ else
+ aPath.setName( lFiles[i] );
+
+ pResultSeq[i-1] = aPath.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ }
+ else
+ aResultSeq = lFiles;
+ }
+
+ return aResultSeq;
+}
+
+OUString FileDialogHelper::GetDisplayDirectory() const
+{
+ return mpImpl->getPath();
+}
+
+OUString FileDialogHelper::GetCurrentFilter() const
+{
+ return mpImpl->getFilter();
+}
+
+ErrCode FileDialogHelper::GetGraphic( Graphic& rGraphic ) const
+{
+ return mpImpl->getGraphic( rGraphic );
+}
+
+static int impl_isFolder( const OUString& rPath )
+{
+ try
+ {
+ ::ucbhelper::Content aContent(
+ rPath, uno::Reference< ucb::XCommandEnvironment > (),
+ comphelper::getProcessComponentContext() );
+ if ( aContent.isFolder() )
+ return 1;
+
+ return 0;
+ }
+ catch ( const Exception & )
+ {
+ }
+
+ return -1;
+}
+
+void FileDialogHelper::SetDisplayDirectory( const OUString& _rPath )
+{
+ if ( _rPath.isEmpty() )
+ return;
+
+ // if the given path isn't a folder, we cut off the last part
+ // and take it as filename and the rest of the path should be
+ // the folder
+
+ INetURLObject aObj( _rPath );
+
+ OUString sFileName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ aObj.removeSegment();
+ OUString sPath = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ int nIsFolder = impl_isFolder( _rPath );
+ if ( nIsFolder == 0 ||
+ ( nIsFolder == -1 && impl_isFolder( sPath ) == 1 ) )
+ {
+ mpImpl->setFileName( sFileName );
+ mpImpl->displayFolder( sPath );
+ }
+ else
+ {
+ INetURLObject aObjPathName( _rPath );
+ OUString sFolder( aObjPathName.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ if ( sFolder.isEmpty() )
+ {
+ // _rPath is not a valid path -> fallback to home directory
+ osl::Security aSecurity;
+ aSecurity.getHomeDir( sFolder );
+ }
+ mpImpl->displayFolder( sFolder );
+ }
+}
+
+void FileDialogHelper::SetDisplayFolder( const OUString& _rURL )
+{
+ mpImpl->displayFolder( _rURL );
+}
+
+void FileDialogHelper::SetFileName( const OUString& _rFileName )
+{
+ mpImpl->setFileName( _rFileName );
+}
+
+void FileDialogHelper::AddFilter( const OUString& rFilterName,
+ const OUString& rExtension )
+{
+ mpImpl->addFilter( rFilterName, rExtension );
+}
+
+void FileDialogHelper::SetCurrentFilter( const OUString& rFilter )
+{
+ OUString sFilter( rFilter );
+ if ( mpImpl->isShowFilterExtensionEnabled() )
+ sFilter = mpImpl->getFilterWithExtension( rFilter );
+ mpImpl->setFilter( sFilter );
+}
+
+const uno::Reference < XFilePicker3 >& FileDialogHelper::GetFilePicker() const
+{
+ return mpImpl->mxFileDlg;
+}
+
+// XFilePickerListener Methods
+void FileDialogHelper::FileSelectionChanged()
+{
+ mpImpl->handleFileSelectionChanged();
+}
+
+void FileDialogHelper::DirectoryChanged()
+{
+ mpImpl->handleDirectoryChanged();
+}
+
+OUString FileDialogHelper::HelpRequested( const FilePickerEvent& aEvent )
+{
+ return sfx2::FileDialogHelper_Impl::handleHelpRequested( aEvent );
+}
+
+void FileDialogHelper::ControlStateChanged( const FilePickerEvent& aEvent )
+{
+ mpImpl->handleControlStateChanged( aEvent );
+}
+
+void FileDialogHelper::DialogSizeChanged()
+{
+ mpImpl->handleDialogSizeChanged();
+}
+
+void FileDialogHelper::DialogClosed( const DialogClosedEvent& _rEvent )
+{
+ m_nError = ( RET_OK == _rEvent.DialogResult ) ? ERRCODE_NONE : ERRCODE_ABORT;
+ m_aDialogClosedLink.Call( this );
+}
+
+ErrCode FileOpenDialog_Impl( weld::Window* pParent,
+ sal_Int16 nDialogType,
+ FileDialogFlags nFlags,
+ std::vector<OUString>& rpURLList,
+ OUString& rFilter,
+ std::optional<SfxAllItemSet>& rpSet,
+ const OUString* pPath,
+ sal_Int16 nDialog,
+ const OUString& rStandardDir,
+ const css::uno::Sequence< OUString >& rDenyList )
+{
+ ErrCode nRet;
+ std::unique_ptr<FileDialogHelper> pDialog;
+ // Sign existing PDF: only works with PDF files and they are opened
+ // read-only to discourage editing (which would invalidate existing
+ // signatures).
+ if (nFlags & FileDialogFlags::SignPDF)
+ pDialog.reset(new FileDialogHelper(nDialogType, nFlags, SfxResId(STR_SFX_FILTERNAME_PDF), u"pdf", rStandardDir, rDenyList, pParent));
+ else
+ pDialog.reset(new FileDialogHelper(nDialogType, nFlags, OUString(), nDialog, SfxFilterFlags::NONE, SfxFilterFlags::NONE, rStandardDir, rDenyList, pParent));
+
+ OUString aPath;
+ if ( pPath )
+ aPath = *pPath;
+
+ nRet = pDialog->Execute(rpURLList, rpSet, rFilter, aPath);
+ DBG_ASSERT( rFilter.indexOf(": ") == -1, "Old filter name used!");
+
+ if (rpSet && nFlags & FileDialogFlags::SignPDF)
+ rpSet->Put(SfxBoolItem(SID_DOC_READONLY, true));
+ return nRet;
+}
+
+ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter, OUString const & aURL, SfxItemSet* pSet, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ uno::Reference<task::XInteractionHandler2> xInteractionHandler = task::InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), rParent);
+ // TODO: need a save way to distinguish MS filters from other filters
+ // for now MS-filters are the only alien filters that support encryption
+ const bool bMSType = !pCurrentFilter->IsOwnFormat();
+ // For OOXML we can use the standard password ("unlimited" characters)
+ // request, otherwise the MS limited password request is needed.
+ const bool bOOXML = bMSType && lclSupportsOOXMLEncryption( pCurrentFilter->GetFilterName());
+ const ::comphelper::DocPasswordRequestType eType = bMSType && !bOOXML ?
+ ::comphelper::DocPasswordRequestType::MS :
+ ::comphelper::DocPasswordRequestType::Standard;
+
+ ::rtl::Reference< ::comphelper::DocPasswordRequest > pPasswordRequest( new ::comphelper::DocPasswordRequest( eType, css::task::PasswordRequestMode_PASSWORD_CREATE, aURL, bool( pCurrentFilter->GetFilterFlags() & SfxFilterFlags::PASSWORDTOMODIFY ) ) );
+
+ uno::Reference< css::task::XInteractionRequest > rRequest( pPasswordRequest );
+ do
+ {
+ xInteractionHandler->handle( rRequest );
+ if (!pPasswordRequest->isPassword() || bMSType)
+ {
+ break;
+ }
+ OString const utf8Pwd(OUStringToOString(pPasswordRequest->getPassword(), RTL_TEXTENCODING_UTF8));
+ OString const utf8Ptm(OUStringToOString(pPasswordRequest->getPasswordToModify(), RTL_TEXTENCODING_UTF8));
+ if (!(52 <= utf8Pwd.getLength() && utf8Pwd.getLength() <= 55
+ && GetODFSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
+ && (52 > utf8Ptm.getLength() || utf8Ptm.getLength() > 55))
+ {
+ break;
+ }
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(Application::GetFrameWeld(rParent), VclMessageType::Warning,
+ VclButtonsType::Ok, SfxResId(STR_PASSWORD_LEN)));
+ xBox->set_secondary_text(SfxResId(STR_PASSWORD_WARNING));
+ xBox->run();
+ }
+ while (true);
+ if ( pPasswordRequest->isPassword() )
+ {
+ if ( pPasswordRequest->getPassword().getLength() )
+ {
+ css::uno::Sequence< css::beans::NamedValue > aEncryptionData;
+
+ // TODO/LATER: The filters should show the password dialog themself in future
+ if ( bMSType )
+ {
+ if (bOOXML)
+ {
+ ::comphelper::SequenceAsHashMap aHashData;
+ aHashData[ OUString( "OOXPassword" ) ] <<= pPasswordRequest->getPassword();
+ aHashData[ OUString( "CryptoType" ) ] <<= OUString( "Standard" );
+ aEncryptionData = aHashData.getAsConstNamedValueList();
+ }
+ else
+ {
+ uno::Sequence< sal_Int8 > aUniqueID = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
+ uno::Sequence< sal_Int8 > aEncryptionKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( pPasswordRequest->getPassword(), aUniqueID );
+
+ if ( aEncryptionKey.hasElements() )
+ {
+ ::comphelper::SequenceAsHashMap aHashData;
+ aHashData[ OUString( "STD97EncryptionKey" ) ] <<= aEncryptionKey;
+ aHashData[ OUString( "STD97UniqueID" ) ] <<= aUniqueID;
+
+ aEncryptionData = aHashData.getAsConstNamedValueList();
+ }
+ else
+ {
+ return ERRCODE_IO_NOTSUPPORTED;
+ }
+ }
+ }
+
+ // tdf#118639: We need ODF encryption data for autorecovery where password will already
+ // be unavailable, even for non-ODF documents, so append it here unconditionally
+ pSet->Put(SfxUnoAnyItem(
+ SID_ENCRYPTIONDATA,
+ uno::Any(comphelper::concatSequences(
+ aEncryptionData, comphelper::OStorageHelper::CreatePackageEncryptionData(
+ pPasswordRequest->getPassword())))));
+ }
+
+ if ( pPasswordRequest->getRecommendReadOnly() )
+ pSet->Put( SfxBoolItem( SID_RECOMMENDREADONLY, true ) );
+
+ if ( bMSType )
+ {
+ if (bOOXML)
+ {
+ uno::Sequence<beans::PropertyValue> aModifyPasswordInfo
+ = ::comphelper::DocPasswordHelper::GenerateNewModifyPasswordInfoOOXML(
+ pPasswordRequest->getPasswordToModify());
+ if (aModifyPasswordInfo.hasElements())
+ pSet->Put(
+ SfxUnoAnyItem(SID_MODIFYPASSWORDINFO, uno::Any(aModifyPasswordInfo)));
+ }
+ else
+ {
+ // the empty password has 0 as Hash
+ sal_Int32 nHash = SfxMedium::CreatePasswordToModifyHash(
+ pPasswordRequest->getPasswordToModify(),
+ pCurrentFilter->GetServiceName() == "com.sun.star.text.TextDocument");
+ if (nHash)
+ pSet->Put(SfxUnoAnyItem(SID_MODIFYPASSWORDINFO, uno::Any(nHash)));
+ }
+ }
+ else
+ {
+ uno::Sequence< beans::PropertyValue > aModifyPasswordInfo = ::comphelper::DocPasswordHelper::GenerateNewModifyPasswordInfo( pPasswordRequest->getPasswordToModify() );
+ if ( aModifyPasswordInfo.hasElements() )
+ pSet->Put( SfxUnoAnyItem( SID_MODIFYPASSWORDINFO, uno::Any( aModifyPasswordInfo ) ) );
+ }
+ }
+ else
+ return ERRCODE_ABORT;
+ return ERRCODE_NONE;
+}
+
+OUString EncodeSpaces_Impl( const OUString& rSource )
+{
+ OUString sRet = rSource.replaceAll( " ", "%20" );
+ return sRet;
+}
+
+OUString DecodeSpaces_Impl( const OUString& rSource )
+{
+ OUString sRet = rSource.replaceAll( "%20", " " );
+ return sRet;
+}
+
+} // end of namespace sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/filedlgimpl.hxx b/sfx2/source/dialog/filedlgimpl.hxx
new file mode 100644
index 000000000..e5910790c
--- /dev/null
+++ b/sfx2/source/dialog/filedlgimpl.hxx
@@ -0,0 +1,220 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SFX2_SOURCE_DIALOG_FILEDLGIMPL_HXX
+#define INCLUDED_SFX2_SOURCE_DIALOG_FILEDLGIMPL_HXX
+
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/graph.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerListener.hpp>
+#include <com/sun/star/ui/dialogs/XDialogClosedListener.hpp>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/filedlghelper.hxx>
+
+class SfxFilterMatcher;
+class GraphicFilter;
+class FileDialogHelper;
+struct ImplSVEvent;
+
+namespace sfx2
+{
+ class FileDialogHelper_Impl :
+ public ::cppu::WeakImplHelper<
+ css::ui::dialogs::XFilePickerListener,
+ css::ui::dialogs::XDialogClosedListener >
+ {
+ friend class FileDialogHelper;
+
+ css::uno::Reference < css::ui::dialogs::XFilePicker3 > mxFileDlg;
+ css::uno::Reference < css::container::XNameAccess > mxFilterCFG;
+
+ std::vector< css::beans::StringPair > maFilters;
+
+ SfxFilterMatcher* mpMatcher;
+ std::unique_ptr<GraphicFilter> mpGraphicFilter;
+ FileDialogHelper* mpAntiImpl;
+ weld::Window* mpFrameWeld;
+
+ ::std::vector< OUString > mlLastURLs;
+
+ OUString maPath;
+ OUString maFileName;
+ OUString maCurFilter;
+ OUString maSelectFilter;
+ OUString maButtonLabel;
+
+ Idle maPreviewIdle;
+ Graphic maGraphic;
+
+ const short m_nDialogType;
+
+ SfxFilterFlags m_nMustFlags;
+ SfxFilterFlags m_nDontFlags;
+
+ ImplSVEvent * mnPostUserEventId;
+
+ FileDialogHelper::Context meContext;
+
+ bool mbHasPassword : 1;
+ bool mbIsPwdEnabled : 1;
+ bool m_bHaveFilterOptions : 1;
+ bool mbHasVersions : 1;
+ bool mbHasAutoExt : 1;
+ bool mbHasPreview : 1;
+ bool mbShowPreview : 1;
+ bool mbIsSaveDlg : 1;
+ bool mbExport : 1;
+
+ bool mbDeleteMatcher : 1;
+ bool mbInsert : 1;
+ bool mbSystemPicker : 1;
+ bool mbAsyncPicker : 1;
+ bool mbPwdCheckBoxState : 1;
+ bool mbSelection : 1;
+ bool mbSelectionEnabled : 1;
+ bool mbHasSelectionBox : 1;
+ bool mbSelectionFltrEnabled : 1;
+
+ private:
+ void addFilters( const OUString& rFactory,
+ SfxFilterFlags nMust,
+ SfxFilterFlags nDont );
+ void addFilter( const OUString& rFilterName,
+ const OUString& rExtension );
+ void addGraphicFilter();
+ void enablePasswordBox( bool bInit );
+ void updateFilterOptionsBox();
+ void updateExportButton();
+ void updateSelectionBox();
+ void updateVersions();
+ void updatePreviewState( bool _bUpdatePreviewWindow );
+ void dispose();
+
+ void loadConfig();
+ void saveConfig();
+
+ std::shared_ptr<const SfxFilter> getCurentSfxFilter();
+ bool updateExtendedControl( sal_Int16 _nExtendedControlId, bool _bEnable );
+
+ ErrCode getGraphic( const OUString& rURL, Graphic& rGraphic ) const;
+ void setDefaultValues();
+
+ void preExecute();
+ void postExecute( sal_Int16 _nResult );
+ sal_Int16 implDoExecute();
+ void implStartExecute();
+
+ void setControlHelpIds( const sal_Int16* _pControlId, const char** _pHelpId );
+
+ bool CheckFilterOptionsCapability( const std::shared_ptr<const SfxFilter>& _pFilter );
+
+ bool isInOpenMode() const;
+ OUString getCurrentFilterUIName() const;
+
+ void LoadLastUsedFilter( const OUString& _rContextIdentifier );
+ void SaveLastUsedFilter();
+
+ void implInitializeFileName( );
+
+ void verifyPath( );
+
+ void implGetAndCacheFiles( const css::uno::Reference< XInterface >& xPicker ,
+ std::vector<OUString>& rpURLList );
+
+ DECL_LINK( TimeOutHdl_Impl, Timer *, void);
+ DECL_LINK( InitControls, void*, void );
+
+ public:
+ // XFilePickerListener methods
+ virtual void SAL_CALL fileSelectionChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) override;
+ virtual void SAL_CALL directoryChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) override;
+ virtual OUString SAL_CALL helpRequested( const css::ui::dialogs::FilePickerEvent& aEvent ) override;
+ virtual void SAL_CALL controlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) override;
+ virtual void SAL_CALL dialogSizeChanged() override;
+
+ // XDialogClosedListener methods
+ virtual void SAL_CALL dialogClosed( const css::ui::dialogs::DialogClosedEvent& _rEvent ) override;
+
+ // XEventListener methods
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // handle XFilePickerListener events
+ void handleFileSelectionChanged();
+ void handleDirectoryChanged();
+ static OUString handleHelpRequested( const css::ui::dialogs::FilePickerEvent& aEvent );
+ void handleControlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent );
+ void handleDialogSizeChanged();
+
+ // Own methods
+ FileDialogHelper_Impl(
+ FileDialogHelper* _pAntiImpl,
+ const sal_Int16 nDialogType,
+ FileDialogFlags nFlags,
+ sal_Int16 nDialog,
+ weld::Window* pFrameWeld,
+ const OUString& sStandardDir = OUString(),
+ const css::uno::Sequence< OUString >& rDenyList = css::uno::Sequence< OUString >()
+ );
+ virtual ~FileDialogHelper_Impl() override;
+
+ ErrCode execute( std::vector<OUString>& rpURLList,
+ std::optional<SfxAllItemSet>& rpSet,
+ OUString& rFilter );
+ ErrCode execute();
+
+ void setFilter( const OUString& rFilter );
+
+ /** sets the directory which should be browsed
+
+ <p>If the given path does not point to a valid (existent and accessible) folder, the request
+ is silently dropped</p>
+ */
+ void displayFolder( const OUString& rPath );
+ void setFileName( const OUString& _rFile );
+
+ OUString getPath() const;
+ OUString getFilter() const;
+ void getRealFilter( OUString& _rFilter ) const;
+
+ ErrCode getGraphic( Graphic& rGraphic ) const;
+ void createMatcher( const OUString& rFactory );
+
+ bool isShowFilterExtensionEnabled() const;
+ void addFilterPair( const OUString& rFilter,
+ const OUString& rFilterWithExtension );
+ OUString getFilterName( std::u16string_view rFilterWithExtension ) const;
+ OUString getFilterWithExtension( std::u16string_view rFilter ) const;
+
+ void SetContext( FileDialogHelper::Context _eNewContext );
+ OUString getInitPath( std::u16string_view _rFallback, const sal_Int32 _nFallbackToken );
+
+ bool isAsyncFilePicker() const { return mbAsyncPicker; }
+ bool isPasswordEnabled() const { return mbIsPwdEnabled; }
+
+ css::uno::Reference<css::awt::XWindow> GetFrameInterface();
+ };
+
+} // end of namespace sfx2
+
+#endif // INCLUDED_SFX2_SOURCE_DIALOG_FILEDLGIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/filtergrouping.cxx b/sfx2/source/dialog/filtergrouping.cxx
new file mode 100644
index 000000000..ef1a4fef3
--- /dev/null
+++ b/sfx2/source/dialog/filtergrouping.cxx
@@ -0,0 +1,1169 @@
+/* -*- 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 "filtergrouping.hxx"
+#include <o3tl/safeint.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/ui/dialogs/XFilterGroupManager.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <unotools/confignode.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/string.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+
+#include <list>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+
+namespace sfx2
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::ui::dialogs;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::utl;
+
+
+ /**
+
+ Some general words about what's going on here...
+
+ <p>In our file open dialog, usually we display every filter we know. That's how it was before: every filter
+ lead to an own line in the filter list box, e.g. "StarWriter 5.0 Document" or "Microsoft Word 97".</p>
+
+ <p>But then the PM came. And everything changed...</p>
+
+ <p>A basic idea are groups: Why simply listing all the single filters? Couldn't we draw nice separators
+ between the filters which logically belong together? I.e. all the filters which open a document in StarWriter:
+ couldn't we separate them from all the filters which open the document in StarCalc?<br/>
+ So spoke the PM, and engineering obeyed.</p>
+
+ <p>So we have groups. They're just a visual aspect: All the filters of a group are presented together, separated
+ by a line from other groups.</p>
+
+ <p>Let's be honest: How the concrete implementation of the file picker service separates the different groups
+ is a matter of this implementation. We only do this grouping and suggest it to the FilePicker service ...</p>
+
+ <p>Now for the second concept:<br/>
+ Thinking about it (and that's what the PM did), both "StarWriter 5.0 Document" and "Microsoft Word 97"
+ describe a text document. It's a text. It's of no interest for the user that one of the texts was saved in
+ MS' format, and one in our own format.<br/>
+ So in a first step, we want to have a filter entry "Text documents". This would cover both above-mentioned
+ filters, as well as any other filters for documents which are texts.</p>
+
+ <p>Such an entry as "Text documents" is - within the scope of this file - called "class" or "filter class".</p>
+
+ <p>In the file-open-dialog, such a class looks like an ordinary filter: it's simply a name in the filter
+ listbox. Selecting means that all the files matching one of the "sub-filters" are displayed (in the example above,
+ this would be "*.sdw", "*.doc" and so on).</p>
+
+ <p>Now there are two types of filter classes: global ones and local ones. "Text documents" is a global class. As
+ well as "Spreadsheets". Or "Web pages".<br/>
+ Let's have a look at a local class: The filters "MS Word 95" and "MS WinWord 6.0" together form the class
+ "Microsoft Word 6.0 / 95" (don't ask for the reasons. At least not me. Ask the PM). There are a lot of such
+ local classes ...</p>
+
+ <p>The difference between global and local classes is as follows: Global classes are presented in an own group.
+ There is one dedicated group at the top of the list, containing all the global groups - no local groups and no
+ single filters.</p>
+
+ <p>Ehm - it was a lie. Not really at the top. Before this group, there is this single "All files" entry. It forms
+ its own group. But this is uninteresting here.</p>
+
+ <p>Local classes must consist of filters which - without the classification - would all belong to the same group.
+ Then, they're combined to one entry (in the example above: "Microsoft Word 6.0 / 95"), and this entry is inserted
+ into the file picker filter list, instead of the single filters which form the class.</p>
+
+ <p>This is an interesting difference between local and global classes: Filters which are part of a global class
+ are listed in their own group, too. Filters in local classes aren't listed a second time - neither directly (as
+ the filter itself) nor indirectly (as part of another local group).</p>
+
+ <p>The only exception are filters which are part of a global class <em>and</em> a local class. This is allowed.
+ Being contained in two local classes isn't.</p>
+
+ <p>So that's all what you need to know: Understand the concept of "filter classes" (a filter class combines
+ different filters and acts as if it's a filter itself) and the concept of groups (a group just describes a
+ logical correlation of filters and usually is represented to the user by drawing group separators in the filter
+ list).</p>
+
+ <p>If you got it, go try understanding this file :).</p>
+
+ */
+
+
+ typedef StringPair FilterDescriptor; // a single filter or a filter class (display name and filter mask)
+ typedef ::std::list< FilterDescriptor > FilterGroup; // a list of single filter entries
+ typedef ::std::list< FilterGroup > GroupedFilterList; // a list of all filters, already grouped
+
+ /// the logical name of a filter
+ typedef OUString FilterName;
+
+ // a struct which holds references from a logical filter name to a filter group entry
+ // used for quick lookup of classes (means class entries - entries representing a class)
+ // which a given filter may belong to
+ typedef ::std::map< OUString, FilterGroup::iterator > FilterGroupEntryReferrer;
+
+ namespace {
+
+ /// a descriptor for a filter class (which in the final dialog is represented by one filter entry)
+ struct FilterClass
+ {
+ OUString sDisplayName; // the display name
+ Sequence< FilterName > aSubFilters; // the (logical) names of the filter which belong to the class
+ };
+
+ }
+
+ typedef ::std::list< FilterClass > FilterClassList;
+ typedef ::std::map< OUString, FilterClassList::iterator > FilterClassReferrer;
+
+
+// = reading of configuration data
+
+
+ static void lcl_ReadFilterClass( const OConfigurationNode& _rClassesNode, const OUString& _rLogicalClassName,
+ FilterClass& /* [out] */ _rClass )
+ {
+ // the description node for the current class
+ OConfigurationNode aClassDesc = _rClassesNode.openNode( _rLogicalClassName );
+
+ // the values
+ aClassDesc.getNodeValue( "DisplayName" ) >>= _rClass.sDisplayName;
+ aClassDesc.getNodeValue( "Filters" ) >>= _rClass.aSubFilters;
+ }
+
+ namespace {
+
+ struct CreateEmptyClassRememberPos
+ {
+ protected:
+ FilterClassList& m_rClassList;
+ FilterClassReferrer& m_rClassesReferrer;
+
+ public:
+ CreateEmptyClassRememberPos( FilterClassList& _rClassList, FilterClassReferrer& _rClassesReferrer )
+ :m_rClassList ( _rClassList )
+ ,m_rClassesReferrer ( _rClassesReferrer )
+ {
+ }
+
+ // operate on a single class name
+ void operator() ( const FilterName& _rLogicalFilterName )
+ {
+ // insert a new (empty) class
+ m_rClassList.emplace_back( );
+ // get the position of this new entry
+ FilterClassList::iterator aInsertPos = m_rClassList.end();
+ --aInsertPos;
+ // remember this position
+ m_rClassesReferrer.emplace( _rLogicalFilterName, aInsertPos );
+ }
+ };
+
+
+ struct ReadGlobalFilter
+ {
+ protected:
+ OConfigurationNode m_aClassesNode;
+ FilterClassReferrer& m_aClassReferrer;
+
+ public:
+ ReadGlobalFilter( const OConfigurationNode& _rClassesNode, FilterClassReferrer& _rClassesReferrer )
+ :m_aClassesNode ( _rClassesNode )
+ ,m_aClassReferrer ( _rClassesReferrer )
+ {
+ }
+
+ // operate on a single logical name
+ void operator() ( const FilterName& _rName )
+ {
+ FilterClassReferrer::iterator aClassRef = m_aClassReferrer.find( _rName );
+ if ( m_aClassReferrer.end() == aClassRef )
+ {
+ // we do not know this global class
+ OSL_FAIL( "ReadGlobalFilter::operator(): unknown filter name!" );
+ // TODO: perhaps we should be more tolerant - at the moment, the filter is dropped
+ // We could silently push_back it to the container...
+ }
+ else
+ {
+ // read the data of this class into the node referred to by aClassRef
+ lcl_ReadFilterClass( m_aClassesNode, _rName, *aClassRef->second );
+ }
+ }
+ };
+
+ }
+
+ static void lcl_ReadGlobalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rGlobalClasses, std::vector<OUString>& _rGlobalClassNames )
+ {
+ _rGlobalClasses.clear();
+ _rGlobalClassNames.clear();
+
+ // get the list describing the order of all global classes
+ Sequence< OUString > aGlobalClasses;
+ _rFilterClassification.getNodeValue( "GlobalFilters/Order" ) >>= aGlobalClasses;
+
+ // copy the logical names
+ comphelper::sequenceToContainer(_rGlobalClassNames, aGlobalClasses);
+
+ // Global classes are presented in an own group, so their order matters (while the order of the
+ // "local classes" doesn't).
+ // That's why we can't simply add the global classes to _rGlobalClasses using the order in which they
+ // are returned from the configuration - it is completely undefined, and we need a _defined_ order.
+ FilterClassReferrer aClassReferrer;
+ ::std::for_each(
+ std::cbegin(aGlobalClasses),
+ std::cend(aGlobalClasses),
+ CreateEmptyClassRememberPos( _rGlobalClasses, aClassReferrer )
+ );
+ // now _rGlobalClasses contains a dummy entry for each global class,
+ // while aClassReferrer maps from the logical name of the class to the position within _rGlobalClasses where
+ // it's dummy entry resides
+
+
+ // go for all the single class entries
+ OConfigurationNode aFilterClassesNode =
+ _rFilterClassification.openNode( "GlobalFilters/Classes" );
+ const Sequence< OUString > aFilterClasses = aFilterClassesNode.getNodeNames();
+ ::std::for_each(
+ aFilterClasses.begin(),
+ aFilterClasses.end(),
+ ReadGlobalFilter( aFilterClassesNode, aClassReferrer )
+ );
+ }
+
+ namespace {
+
+ struct ReadLocalFilter
+ {
+ protected:
+ OConfigurationNode m_aClassesNode;
+ FilterClassList& m_rClasses;
+
+ public:
+ ReadLocalFilter( const OConfigurationNode& _rClassesNode, FilterClassList& _rClasses )
+ :m_aClassesNode ( _rClassesNode )
+ ,m_rClasses ( _rClasses )
+ {
+ }
+
+ // operate on a single logical name
+ void operator() ( const FilterName& _rName )
+ {
+ // read the data for this class
+ FilterClass aClass;
+ lcl_ReadFilterClass( m_aClassesNode, _rName, aClass );
+
+ // insert the class descriptor
+ m_rClasses.push_back( aClass );
+ }
+ };
+
+ }
+
+ static void lcl_ReadLocalFilters( const OConfigurationNode& _rFilterClassification, FilterClassList& _rLocalClasses )
+ {
+ _rLocalClasses.clear();
+
+ // the node for the local classes
+ OConfigurationNode aFilterClassesNode =
+ _rFilterClassification.openNode( "LocalFilters/Classes" );
+ const Sequence< OUString > aFilterClasses = aFilterClassesNode.getNodeNames();
+
+ ::std::for_each(
+ aFilterClasses.begin(),
+ aFilterClasses.end(),
+ ReadLocalFilter( aFilterClassesNode, _rLocalClasses )
+ );
+ }
+
+
+ static void lcl_ReadClassification( FilterClassList& _rGlobalClasses, std::vector<OUString>& _rGlobalClassNames, FilterClassList& _rLocalClasses )
+ {
+
+ // open our config node
+ OConfigurationTreeRoot aFilterClassification = OConfigurationTreeRoot::createWithComponentContext(
+ ::comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.UI/FilterClassification",
+ -1,
+ OConfigurationTreeRoot::CM_READONLY
+ );
+
+
+ // go for the global classes
+ lcl_ReadGlobalFilters( aFilterClassification, _rGlobalClasses, _rGlobalClassNames );
+
+
+ // go for the local classes
+ lcl_ReadLocalFilters( aFilterClassification, _rLocalClasses );
+
+ }
+
+
+// = grouping and classifying
+
+ namespace {
+
+ // a struct which adds helps remembering a reference to a class entry
+ struct ReferToFilterEntry
+ {
+ protected:
+ FilterGroupEntryReferrer& m_rEntryReferrer;
+ FilterGroup::iterator m_aClassPos;
+
+ public:
+ ReferToFilterEntry( FilterGroupEntryReferrer& _rEntryReferrer, const FilterGroup::iterator& _rClassPos )
+ :m_rEntryReferrer( _rEntryReferrer )
+ ,m_aClassPos( _rClassPos )
+ {
+ }
+
+ // operate on a single filter name
+ void operator() ( const FilterName& _rName )
+ {
+ ::std::pair< FilterGroupEntryReferrer::iterator, bool > aInsertRes =
+ m_rEntryReferrer.emplace( _rName, m_aClassPos );
+ SAL_WARN_IF(
+ !aInsertRes.second, "sfx.dialog",
+ "already have an element for " << _rName);
+ }
+ };
+
+
+ struct FillClassGroup
+ {
+ protected:
+ FilterGroup& m_rClassGroup;
+ FilterGroupEntryReferrer& m_rClassReferrer;
+
+ public:
+ FillClassGroup( FilterGroup& _rClassGroup, FilterGroupEntryReferrer& _rClassReferrer )
+ :m_rClassGroup ( _rClassGroup )
+ ,m_rClassReferrer ( _rClassReferrer )
+ {
+ }
+
+ // operate on a single class
+ void operator() ( const FilterClass& _rClass )
+ {
+ // create an empty filter descriptor for the class
+ FilterDescriptor aClassEntry;
+ // set its name (which is all we know by now)
+ aClassEntry.First = _rClass.sDisplayName;
+
+ // add it to the group
+ m_rClassGroup.push_back( aClassEntry );
+ // the position of the newly added class
+ FilterGroup::iterator aClassEntryPos = m_rClassGroup.end();
+ --aClassEntryPos;
+
+ // and for all the sub filters of the class, remember the class
+ // (respectively the position of the class it the group)
+ ::std::for_each(
+ _rClass.aSubFilters.begin(),
+ _rClass.aSubFilters.end(),
+ ReferToFilterEntry( m_rClassReferrer, aClassEntryPos )
+ );
+ }
+ };
+
+ }
+
+ const sal_Unicode s_cWildcardSeparator( ';' );
+
+ static OUString getSeparatorString()
+ {
+ return ";";
+ }
+
+ namespace {
+
+ struct CheckAppendSingleWildcard
+ {
+ OUString& _rToBeExtended;
+
+ explicit CheckAppendSingleWildcard( OUString& _rBase ) : _rToBeExtended( _rBase ) { }
+
+ void operator() ( const OUString& _rWC )
+ {
+ // check for double wildcards
+ sal_Int32 nExistentPos = _rToBeExtended.indexOf( _rWC );
+ if ( -1 < nExistentPos )
+ { // found this wildcard (already part of _rToBeExtended)
+ if ( ( 0 == nExistentPos )
+ || ( s_cWildcardSeparator == _rToBeExtended[ nExistentPos - 1 ] )
+ )
+ { // the wildcard really starts at this position (it starts at pos 0 or the previous character is a separator
+ sal_Int32 nExistentWCEnd = nExistentPos + _rWC.getLength();
+ if ( ( _rToBeExtended.getLength() == nExistentWCEnd )
+ || ( s_cWildcardSeparator == _rToBeExtended[ nExistentWCEnd ] )
+ )
+ { // it's really the complete wildcard we found
+ // (not something like _rWC being "*.t" and _rToBeExtended containing "*.txt")
+ // -> outta here
+ return;
+ }
+ }
+ }
+
+ if ( !_rToBeExtended.isEmpty() )
+ _rToBeExtended += getSeparatorString();
+ _rToBeExtended += _rWC;
+ }
+ };
+
+
+ // a helper struct which adds a fixed (Sfx-)filter to a filter group entry given by iterator
+ struct AppendWildcardToDescriptor
+ {
+ protected:
+ ::std::vector< OUString > aWildCards;
+
+ public:
+ explicit AppendWildcardToDescriptor( const OUString& _rWildCard );
+
+ // operate on a single class entry
+ void operator() ( const FilterGroupEntryReferrer::value_type& _rClassReference )
+ {
+ // simply add our wildcards
+ ::std::for_each(
+ aWildCards.begin(),
+ aWildCards.end(),
+ CheckAppendSingleWildcard( _rClassReference.second->Second )
+ );
+ }
+ };
+
+ }
+
+ AppendWildcardToDescriptor::AppendWildcardToDescriptor( const OUString& _rWildCard )
+ {
+ DBG_ASSERT( !_rWildCard.isEmpty(),
+ "AppendWildcardToDescriptor::AppendWildcardToDescriptor: invalid wildcard!" );
+ DBG_ASSERT( _rWildCard.isEmpty() || _rWildCard[0] != s_cWildcardSeparator,
+ "AppendWildcardToDescriptor::AppendWildcardToDescriptor: wildcard already separated!" );
+
+ aWildCards.reserve( comphelper::string::getTokenCount(_rWildCard, s_cWildcardSeparator) );
+
+ const sal_Unicode* pTokenLoop = _rWildCard.getStr();
+ const sal_Unicode* pTokenLoopEnd = pTokenLoop + _rWildCard.getLength();
+ const sal_Unicode* pTokenStart = pTokenLoop;
+ for ( ; pTokenLoop != pTokenLoopEnd; ++pTokenLoop )
+ {
+ if ( ( s_cWildcardSeparator == *pTokenLoop ) && ( pTokenLoop > pTokenStart ) )
+ { // found a new token separator (and a non-empty token)
+ aWildCards.emplace_back( pTokenStart, pTokenLoop - pTokenStart );
+
+ // search the start of the next token
+ while ( ( pTokenStart != pTokenLoopEnd ) && ( *pTokenStart != s_cWildcardSeparator ) )
+ ++pTokenStart;
+
+ if ( pTokenStart == pTokenLoopEnd )
+ // reached the end
+ break;
+
+ ++pTokenStart;
+ pTokenLoop = pTokenStart;
+ }
+ }
+ if ( pTokenLoop > pTokenStart )
+ // the last one...
+ aWildCards.emplace_back( pTokenStart, pTokenLoop - pTokenStart );
+ }
+
+
+ static void lcl_InitGlobalClasses( GroupedFilterList& _rAllFilters, const FilterClassList& _rGlobalClasses, FilterGroupEntryReferrer& _rGlobalClassesRef )
+ {
+ // we need an extra group in our "all filters" container
+ _rAllFilters.push_front( FilterGroup() );
+ FilterGroup& rGlobalFilters = _rAllFilters.front();
+ // it's important to work on the reference: we want to access the members of this filter group
+ // by an iterator (FilterGroup::const_iterator)
+ // the referrer for the global classes
+
+ // initialize the group
+ ::std::for_each(
+ _rGlobalClasses.begin(),
+ _rGlobalClasses.end(),
+ FillClassGroup( rGlobalFilters, _rGlobalClassesRef )
+ );
+ // now we have:
+ // in rGlobalFilters: a list of FilterDescriptor's, where each's descriptor's display name is set to the name of a class
+ // in aGlobalClassesRef: a mapping from logical filter names to positions within rGlobalFilters
+ // this way, if we encounter an arbitrary filter, we can easily (and efficient) check if it belongs to a global class
+ // and modify the descriptor for this class accordingly
+ }
+
+
+ typedef ::std::vector< ::std::pair< FilterGroupEntryReferrer::mapped_type, FilterGroup::iterator > >
+ MapGroupEntry2GroupEntry;
+ // this is not really a map - it's just called this way because it is used as a map
+
+ namespace {
+
+ struct FindGroupEntry
+ {
+ FilterGroupEntryReferrer::mapped_type aLookingFor;
+ explicit FindGroupEntry( FilterGroupEntryReferrer::mapped_type const & _rLookingFor ) : aLookingFor( _rLookingFor ) { }
+
+ bool operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry )
+ {
+ return _rMapEntry.first == aLookingFor;
+ }
+ };
+
+ struct CopyGroupEntryContent
+ {
+ void operator() ( const MapGroupEntry2GroupEntry::value_type& _rMapEntry )
+ {
+ *_rMapEntry.second = *_rMapEntry.first;
+ }
+ };
+
+
+ struct CopyNonEmptyFilter
+ {
+ FilterGroup& rTarget;
+ explicit CopyNonEmptyFilter( FilterGroup& _rTarget ) :rTarget( _rTarget ) { }
+
+ void operator() ( const FilterDescriptor& _rFilter )
+ {
+ if ( !_rFilter.Second.isEmpty() )
+ rTarget.push_back( _rFilter );
+ }
+ };
+
+ }
+
+ static void lcl_GroupAndClassify( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rAllFilters )
+ {
+ _rAllFilters.clear();
+
+
+ // read the classification of filters
+ FilterClassList aGlobalClasses, aLocalClasses;
+ std::vector<OUString> aGlobalClassNames;
+ lcl_ReadClassification( aGlobalClasses, aGlobalClassNames, aLocalClasses );
+
+
+ // for the global filter classes
+ FilterGroupEntryReferrer aGlobalClassesRef;
+ lcl_InitGlobalClasses( _rAllFilters, aGlobalClasses, aGlobalClassesRef );
+
+ // insert as much placeholders (FilterGroup's) into _rAllFilter for groups as we have global classes
+ // (this assumes that both numbers are the same, which, speaking strictly, must not hold - but it does, as we know ...)
+ sal_Int32 nGlobalClasses = aGlobalClasses.size();
+ while ( nGlobalClasses-- )
+ _rAllFilters.emplace_back( );
+
+
+ // for the local classes:
+ // if n filters belong to a local class, they do not appear in their respective group explicitly, instead
+ // and entry for the class is added to the group and the extensions of the filters are collected under
+ // this entry
+ FilterGroupEntryReferrer aLocalClassesRef;
+ FilterGroup aCollectedLocals;
+ ::std::for_each(
+ aLocalClasses.begin(),
+ aLocalClasses.end(),
+ FillClassGroup( aCollectedLocals, aLocalClassesRef )
+ );
+ // to map from the position within aCollectedLocals to positions within the real groups
+ // (where they finally belong to)
+ MapGroupEntry2GroupEntry aLocalFinalPositions;
+
+
+ // now add the filters
+ // the group which we currently work with
+ GroupedFilterList::iterator aCurrentGroup = _rAllFilters.end(); // no current group
+ // the filter container of the current group - if this changes between two filters, a new group is reached
+ OUString aCurrentServiceName;
+
+ OUString sFilterWildcard;
+ OUString sFilterName;
+ // loop through all the filters
+ for ( std::shared_ptr<const SfxFilter> pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
+ {
+ sFilterName = pFilter->GetFilterName();
+ sFilterWildcard = pFilter->GetWildcard().getGlob();
+ AppendWildcardToDescriptor aExtendWildcard( sFilterWildcard );
+
+ DBG_ASSERT( !sFilterWildcard.isEmpty(), "sfx2::lcl_GroupAndClassify: invalid wildcard of this filter!" );
+
+
+ // check for a change in the group
+ OUString aServiceName = pFilter->GetServiceName();
+ if ( aServiceName != aCurrentServiceName )
+ { // we reached a new group
+
+ // look for the place in _rAllFilters where this ne group belongs - this is determined
+ // by the order of classes in aGlobalClassNames
+ GroupedFilterList::iterator aGroupPos = _rAllFilters.begin();
+ DBG_ASSERT( aGroupPos != _rAllFilters.end(),
+ "sfx2::lcl_GroupAndClassify: invalid all-filters array here!" );
+ // the loop below will work on invalid objects else ...
+ ++aGroupPos;
+ auto aGlobalIter = std::find(aGlobalClassNames.begin(), aGlobalClassNames.end(), aServiceName);
+ auto nGroupPosShift = std::min(
+ std::distance(aGlobalClassNames.begin(), aGlobalIter),
+ std::distance(aGroupPos, _rAllFilters.end()));
+ std::advance(aGroupPos, nGroupPosShift);
+ if ( aGroupPos != _rAllFilters.end() )
+ // we found a global class name which matches the doc service name -> fill the filters of this
+ // group in the respective prepared group
+ aCurrentGroup = aGroupPos;
+ else
+ // insert a new entry in our overall-list
+ aCurrentGroup = _rAllFilters.insert( _rAllFilters.end(), FilterGroup() );
+
+ // remember the container to properly detect the next group
+ aCurrentServiceName = aServiceName;
+ }
+
+ assert(aCurrentGroup != _rAllFilters.end()); //invalid current group!
+ if (aCurrentGroup == _rAllFilters.end())
+ aCurrentGroup = _rAllFilters.begin();
+
+
+ // check if the filter is part of a global group
+ ::std::pair< FilterGroupEntryReferrer::iterator, FilterGroupEntryReferrer::iterator >
+ aBelongsTo = aGlobalClassesRef.equal_range( sFilterName );
+ // add the filter to the entries for these classes
+ // (if they exist - if not, the range is empty and the for_each is a no-op)
+ ::std::for_each(
+ aBelongsTo.first,
+ aBelongsTo.second,
+ aExtendWildcard
+ );
+
+
+ // add the filter to its group
+
+ // for this, check if the filter is part of a local filter
+ FilterGroupEntryReferrer::iterator aBelongsToLocal = aLocalClassesRef.find( sFilterName );
+ if ( aLocalClassesRef.end() != aBelongsToLocal )
+ {
+ // okay, there is a local class which the filter belongs to
+ // -> append the wildcard
+ aExtendWildcard( *aBelongsToLocal );
+
+ if ( std::none_of( aLocalFinalPositions.begin(), aLocalFinalPositions.end(), FindGroupEntry( aBelongsToLocal->second ) ) )
+ { // the position within aCollectedLocals has not been mapped to a final position
+ // within the "real" group (aCollectedLocals is only temporary)
+ // -> do this now (as we just encountered the first filter belonging to this local class
+ // add a new entry which is the "real" group entry
+ aCurrentGroup->push_back( FilterDescriptor( aBelongsToLocal->second->First, OUString() ) );
+ // the position where we inserted the entry
+ FilterGroup::iterator aInsertPos = aCurrentGroup->end();
+ --aInsertPos;
+ // remember this pos
+ aLocalFinalPositions.emplace_back( aBelongsToLocal->second, aInsertPos );
+ }
+ }
+ else
+ aCurrentGroup->push_back( FilterDescriptor( pFilter->GetUIName(), sFilterWildcard ) );
+ }
+
+ // now just complete the infos for the local groups:
+ // During the above loop, they have been collected in aCollectedLocals, but this is only temporary
+ // They have to be copied into their final positions (which are stored in aLocalFinalPositions)
+ ::std::for_each(
+ aLocalFinalPositions.begin(),
+ aLocalFinalPositions.end(),
+ CopyGroupEntryContent()
+ );
+
+ // and remove local groups which do not apply - e.g. have no entries due to the limited content of the
+ // current SfxFilterMatcherIter
+
+ FilterGroup& rGlobalFilters = _rAllFilters.front();
+ FilterGroup aNonEmptyGlobalFilters;
+ ::std::for_each(
+ rGlobalFilters.begin(),
+ rGlobalFilters.end(),
+ CopyNonEmptyFilter( aNonEmptyGlobalFilters )
+ );
+ rGlobalFilters.swap( aNonEmptyGlobalFilters );
+ }
+
+ namespace {
+
+ struct AppendFilter
+ {
+ protected:
+ Reference< XFilterManager > m_xFilterManager;
+ FileDialogHelper_Impl* m_pFileDlgImpl;
+ bool m_bAddExtension;
+
+ public:
+ AppendFilter( const Reference< XFilterManager >& _rxFilterManager,
+ FileDialogHelper_Impl* _pImpl, bool _bAddExtension ) :
+
+ m_xFilterManager( _rxFilterManager ),
+ m_pFileDlgImpl ( _pImpl ),
+ m_bAddExtension ( _bAddExtension )
+
+ {
+ DBG_ASSERT( m_xFilterManager.is(), "AppendFilter::AppendFilter: invalid filter manager!" );
+ DBG_ASSERT( m_pFileDlgImpl, "AppendFilter::AppendFilter: invalid filedlg impl!" );
+ }
+
+ // operate on a single filter
+ void operator() ( const FilterDescriptor& _rFilterEntry )
+ {
+ OUString sDisplayText = m_bAddExtension
+ ? addExtension( _rFilterEntry.First, _rFilterEntry.Second, true, *m_pFileDlgImpl )
+ : _rFilterEntry.First;
+ m_xFilterManager->appendFilter( sDisplayText, _rFilterEntry.Second );
+ }
+ };
+
+ }
+
+// = handling for the "all files" entry
+
+
+ static bool lcl_hasAllFilesFilter( TSortedFilterList& _rFilterMatcher, OUString& /* [out] */ _rAllFilterName )
+ {
+ bool bHasAll = false;
+ _rAllFilterName = SfxResId( STR_SFX_FILTERNAME_ALL );
+
+
+ // check if there's already a filter <ALL>
+ for ( std::shared_ptr<const SfxFilter> pFilter = _rFilterMatcher.First(); pFilter && !bHasAll; pFilter = _rFilterMatcher.Next() )
+ {
+ if ( pFilter->GetUIName() == _rAllFilterName )
+ bHasAll = true;
+ }
+ return bHasAll;
+ }
+
+
+ static void lcl_EnsureAllFilesEntry( TSortedFilterList& _rFilterMatcher, GroupedFilterList& _rFilters )
+ {
+
+ OUString sAllFilterName;
+ if ( !lcl_hasAllFilesFilter( _rFilterMatcher, sAllFilterName ) )
+ {
+ // get the first group of filters (by definition, this group contains the global classes)
+ DBG_ASSERT( !_rFilters.empty(), "lcl_EnsureAllFilesEntry: invalid filter list!" );
+ if ( !_rFilters.empty() )
+ {
+ FilterGroup& rGlobalClasses = *_rFilters.begin();
+ rGlobalClasses.push_front( FilterDescriptor( sAllFilterName, FILEDIALOG_FILTER_ALL ) );
+ }
+ }
+ }
+
+
+// = filling an XFilterManager
+
+ namespace {
+
+ struct AppendFilterGroup
+ {
+ protected:
+ Reference< XFilterManager > m_xFilterManager;
+ Reference< XFilterGroupManager > m_xFilterGroupManager;
+ FileDialogHelper_Impl* m_pFileDlgImpl;
+
+ public:
+ AppendFilterGroup( const Reference< XFilterManager >& _rxFilterManager, FileDialogHelper_Impl* _pImpl )
+ :m_xFilterManager ( _rxFilterManager )
+ ,m_xFilterGroupManager ( _rxFilterManager, UNO_QUERY )
+ ,m_pFileDlgImpl ( _pImpl )
+ {
+ DBG_ASSERT( m_xFilterManager.is(), "AppendFilterGroup::AppendFilterGroup: invalid filter manager!" );
+ DBG_ASSERT( m_pFileDlgImpl, "AppendFilterGroup::AppendFilterGroup: invalid filedlg impl!" );
+ }
+
+ void appendGroup( const FilterGroup& _rGroup, bool _bAddExtension )
+ {
+ try
+ {
+ if ( m_xFilterGroupManager.is() )
+ { // the file dialog implementation supports visual grouping of filters
+ // create a representation of the group which is understandable by the XFilterGroupManager
+ if ( !_rGroup.empty() )
+ {
+ Sequence< StringPair > aFilters( comphelper::containerToSequence(_rGroup) );
+ if ( _bAddExtension )
+ {
+ for ( StringPair & filter : asNonConstRange(aFilters) )
+ filter.First = addExtension( filter.First, filter.Second, true, *m_pFileDlgImpl );
+ }
+ m_xFilterGroupManager->appendFilterGroup( OUString(), aFilters );
+ }
+ }
+ else
+ {
+ ::std::for_each(
+ _rGroup.begin(),
+ _rGroup.end(),
+ AppendFilter( m_xFilterManager, m_pFileDlgImpl, _bAddExtension ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sfx.dialog");
+ }
+ }
+
+ // operate on a single filter group
+ void operator() ( const FilterGroup& _rGroup )
+ {
+ appendGroup( _rGroup, true );
+ }
+ };
+
+ }
+
+ TSortedFilterList::TSortedFilterList(const css::uno::Reference< css::container::XEnumeration >& xFilterList)
+ : m_nIterator(0)
+ {
+ if (!xFilterList.is())
+ return;
+
+ m_lFilters.clear();
+ while(xFilterList->hasMoreElements())
+ {
+ ::comphelper::SequenceAsHashMap lFilterProps (xFilterList->nextElement());
+ OUString sFilterName = lFilterProps.getUnpackedValueOrDefault(
+ "Name",
+ OUString());
+ if (!sFilterName.isEmpty())
+ m_lFilters.push_back(sFilterName);
+ }
+ }
+
+
+ std::shared_ptr<const SfxFilter> TSortedFilterList::First()
+ {
+ m_nIterator = 0;
+ return impl_getFilter(m_nIterator);
+ }
+
+
+ std::shared_ptr<const SfxFilter> TSortedFilterList::Next()
+ {
+ ++m_nIterator;
+ return impl_getFilter(m_nIterator);
+ }
+
+
+ std::shared_ptr<const SfxFilter> TSortedFilterList::impl_getFilter(sal_Int32 nIndex)
+ {
+ if (nIndex<0 || o3tl::make_unsigned(nIndex)>=m_lFilters.size())
+ return nullptr;
+ const OUString& sFilterName = m_lFilters[nIndex];
+ if (sFilterName.isEmpty())
+ return nullptr;
+ return SfxFilter::GetFilterByName(sFilterName);
+ }
+
+
+ void appendFiltersForSave( TSortedFilterList& _rFilterMatcher,
+ const Reference< XFilterManager >& _rxFilterManager,
+ OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl,
+ std::u16string_view _rFactory )
+ {
+ DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForSave: invalid manager!" );
+ if ( !_rxFilterManager.is() )
+ return;
+
+ OUString sUIName;
+ OUString sExtension;
+
+ // retrieve the default filter for this application module.
+ // It must be set as first of the generated filter list.
+ std::shared_ptr<const SfxFilter> pDefaultFilter = SfxFilterContainer::GetDefaultFilter_Impl(_rFactory);
+ // Only use one extension (#i32434#)
+ // (and always the first if there are more than one)
+ sExtension = pDefaultFilter->GetWildcard().getGlob().getToken(0, ';');
+ sUIName = addExtension( pDefaultFilter->GetUIName(), sExtension, false, _rFileDlgImpl );
+ try
+ {
+ _rxFilterManager->appendFilter( sUIName, sExtension );
+ if ( _rFirstNonEmpty.isEmpty() )
+ _rFirstNonEmpty = sUIName;
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append DefaultFilter" << sUIName );
+ }
+
+ for ( std::shared_ptr<const SfxFilter> pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
+ {
+ if (pFilter->GetName() == pDefaultFilter->GetName())
+ continue;
+
+ // Only use one extension (#i32434#)
+ // (and always the first if there are more than one)
+ sExtension = pFilter->GetWildcard().getGlob().getToken(0, ';');
+ sUIName = addExtension( pFilter->GetUIName(), sExtension, false, _rFileDlgImpl );
+ try
+ {
+ _rxFilterManager->appendFilter( sUIName, sExtension );
+ if ( _rFirstNonEmpty.isEmpty() )
+ _rFirstNonEmpty = sUIName;
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append Filter" << sUIName );
+ }
+ }
+ }
+
+ namespace {
+
+ struct ExportFilter
+ {
+ ExportFilter( const OUString& _aUIName, const OUString& _aWildcard ) :
+ aUIName( _aUIName ), aWildcard( _aWildcard ) {}
+
+ OUString aUIName;
+ OUString aWildcard;
+ };
+
+ }
+
+ void appendExportFilters( TSortedFilterList& _rFilterMatcher,
+ const Reference< XFilterManager >& _rxFilterManager,
+ OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl )
+ {
+ DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendExportFilters: invalid manager!" );
+ if ( !_rxFilterManager.is() )
+ return;
+
+ sal_Int32 nHTMLIndex = -1;
+ sal_Int32 nXHTMLIndex = -1;
+ sal_Int32 nPDFIndex = -1;
+ OUString sUIName;
+ OUString sExtensions;
+ std::vector< ExportFilter > aImportantFilterGroup;
+ std::vector< ExportFilter > aFilterGroup;
+ Reference< XFilterGroupManager > xFilterGroupManager( _rxFilterManager, UNO_QUERY );
+ OUString sTypeName;
+
+ for ( std::shared_ptr<const SfxFilter> pFilter = _rFilterMatcher.First(); pFilter; pFilter = _rFilterMatcher.Next() )
+ {
+ sTypeName = pFilter->GetTypeName();
+ sUIName = pFilter->GetUIName();
+ sExtensions = pFilter->GetWildcard().getGlob();
+ ExportFilter aExportFilter( sUIName, sExtensions );
+
+ if ( nHTMLIndex == -1 &&
+ ( sTypeName == "generic_HTML" || sTypeName == "graphic_HTML" ) )
+ {
+ aImportantFilterGroup.insert( aImportantFilterGroup.begin(), aExportFilter );
+ nHTMLIndex = 0;
+ }
+ else if ( nXHTMLIndex == -1 && sTypeName == "XHTML_File" )
+ {
+ std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
+ if ( nHTMLIndex == -1 )
+ aImportantFilterGroup.insert( aIter, aExportFilter );
+ else
+ aImportantFilterGroup.insert( ++aIter, aExportFilter );
+ nXHTMLIndex = 0;
+ }
+ else if ( nPDFIndex == -1 && sTypeName == "pdf_Portable_Document_Format" )
+ {
+ std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
+ if ( nHTMLIndex != -1 )
+ ++aIter;
+ if ( nXHTMLIndex != -1 )
+ ++aIter;
+ aImportantFilterGroup.insert( aIter, aExportFilter );
+ nPDFIndex = 0;
+ }
+ else
+ aFilterGroup.push_back( aExportFilter );
+ }
+
+ if ( xFilterGroupManager.is() )
+ {
+ // Add both html/pdf filter as a filter group to get a separator between both groups
+ if ( !aImportantFilterGroup.empty() )
+ {
+ Sequence< StringPair > aFilters( aImportantFilterGroup.size() );
+ auto pFilters = aFilters.getArray();
+ for ( sal_Int32 i = 0; i < static_cast<sal_Int32>(aImportantFilterGroup.size()); i++ )
+ {
+ pFilters[i].First = addExtension( aImportantFilterGroup[i].aUIName,
+ aImportantFilterGroup[i].aWildcard,
+ false, _rFileDlgImpl );
+ pFilters[i].Second = aImportantFilterGroup[i].aWildcard;
+ }
+
+ try
+ {
+ xFilterGroupManager->appendFilterGroup( OUString(), aFilters );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ }
+ }
+
+ if ( !aFilterGroup.empty() )
+ {
+ Sequence< StringPair > aFilters( aFilterGroup.size() );
+ auto pFilters = aFilters.getArray();
+ for ( sal_Int32 i = 0; i < static_cast<sal_Int32>(aFilterGroup.size()); i++ )
+ {
+ pFilters[i].First = addExtension( aFilterGroup[i].aUIName,
+ aFilterGroup[i].aWildcard,
+ false, _rFileDlgImpl );
+ pFilters[i].Second = aFilterGroup[i].aWildcard;
+ }
+
+ try
+ {
+ xFilterGroupManager->appendFilterGroup( OUString(), aFilters );
+ }
+ catch( const IllegalArgumentException& )
+ {
+ }
+ }
+ }
+ else
+ {
+ // Fallback solution just add both filter groups as single filters
+ sal_Int32 n;
+
+ for ( n = 0; n < static_cast<sal_Int32>(aImportantFilterGroup.size()); n++ )
+ {
+ try
+ {
+ OUString aUIName = addExtension( aImportantFilterGroup[n].aUIName,
+ aImportantFilterGroup[n].aWildcard,
+ false, _rFileDlgImpl );
+ _rxFilterManager->appendFilter( aUIName, aImportantFilterGroup[n].aWildcard );
+ if ( _rFirstNonEmpty.isEmpty() )
+ _rFirstNonEmpty = sUIName;
+
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append Filter" << sUIName );
+ }
+ }
+
+ for ( n = 0; n < static_cast<sal_Int32>(aFilterGroup.size()); n++ )
+ {
+ try
+ {
+ OUString aUIName = addExtension( aFilterGroup[n].aUIName,
+ aFilterGroup[n].aWildcard,
+ false, _rFileDlgImpl );
+ _rxFilterManager->appendFilter( aUIName, aFilterGroup[n].aWildcard );
+ if ( _rFirstNonEmpty.isEmpty() )
+ _rFirstNonEmpty = sUIName;
+
+ }
+ catch( const IllegalArgumentException& )
+ {
+ SAL_WARN( "sfx.dialog", "Could not append Filter" << sUIName );
+ }
+ }
+ }
+ }
+
+
+ void appendFiltersForOpen( TSortedFilterList& _rFilterMatcher,
+ const Reference< XFilterManager >& _rxFilterManager,
+ OUString& _rFirstNonEmpty, FileDialogHelper_Impl& _rFileDlgImpl )
+ {
+ DBG_ASSERT( _rxFilterManager.is(), "sfx2::appendFiltersForOpen: invalid manager!" );
+ if ( !_rxFilterManager.is() )
+ return;
+
+
+ // group and classify the filters
+ GroupedFilterList aAllFilters;
+ lcl_GroupAndClassify( _rFilterMatcher, aAllFilters );
+
+
+ // ensure that we have the one "all files" entry
+ lcl_EnsureAllFilesEntry( _rFilterMatcher, aAllFilters );
+
+
+ // the first non-empty string - which we assume is the first overall entry
+ if ( !aAllFilters.empty() )
+ {
+ const FilterGroup& rFirstGroup = *aAllFilters.begin(); // should be the global classes
+ if ( !rFirstGroup.empty() )
+ _rFirstNonEmpty = rFirstGroup.begin()->First;
+ // append first group, without extension
+ AppendFilterGroup aGroup( _rxFilterManager, &_rFileDlgImpl );
+ aGroup.appendGroup( rFirstGroup, false );
+ }
+
+
+ // append the filters to the manager
+ if ( !aAllFilters.empty() )
+ {
+ ::std::list< FilterGroup >::iterator pIter = aAllFilters.begin();
+ ++pIter;
+ ::std::for_each(
+ pIter, // first filter group was handled separately, see above
+ aAllFilters.end(),
+ AppendFilterGroup( _rxFilterManager, &_rFileDlgImpl ) );
+ }
+ }
+
+ OUString addExtension( const OUString& _rDisplayText,
+ const OUString& _rExtension,
+ bool _bForOpen, FileDialogHelper_Impl& _rFileDlgImpl )
+ {
+ OUString sRet = _rDisplayText;
+
+ if ( sRet.indexOf( "(*.*)" ) == -1 )
+ {
+ OUString sExt = _rExtension;
+ if ( !_bForOpen )
+ {
+ // show '*' in extensions only when opening a document
+ sExt = sExt.replaceAll("*", "");
+ }
+ sRet += " (" + sExt + ")";
+ }
+ _rFileDlgImpl.addFilterPair( _rDisplayText, sRet );
+ return sRet;
+ }
+
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/filtergrouping.hxx b/sfx2/source/dialog/filtergrouping.hxx
new file mode 100644
index 000000000..1d8a44473
--- /dev/null
+++ b/sfx2/source/dialog/filtergrouping.hxx
@@ -0,0 +1,96 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SFX2_SOURCE_DIALOG_FILTERGROUPING_HXX
+#define INCLUDED_SFX2_SOURCE_DIALOG_FILTERGROUPING_HXX
+
+#include <com/sun/star/ui/dialogs/XFilterManager.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include "filedlgimpl.hxx"
+
+#include <memory>
+
+namespace sfx2
+{
+
+
+ class TSortedFilterList
+ {
+ private:
+ ::std::vector< OUString > m_lFilters;
+ sal_Int32 m_nIterator;
+
+ public:
+ explicit TSortedFilterList(const css::uno::Reference< css::container::XEnumeration >& xFilterList);
+ std::shared_ptr<const SfxFilter> First();
+ std::shared_ptr<const SfxFilter> Next();
+
+ private:
+ std::shared_ptr<const SfxFilter> impl_getFilter(sal_Int32 nIndex);
+ };
+
+
+ /** adds the given filters to the filter manager.
+ <p>To be used when saving generic files.</p>
+ */
+ void appendFiltersForSave(
+ TSortedFilterList& _rFilterMatcher,
+ const css::uno::Reference< css::ui::dialogs::XFilterManager >& _rFilterManager,
+ OUString& /* [out] */ _rFirstNonEmpty,
+ FileDialogHelper_Impl& _rFileDlgImpl,
+ std::u16string_view _rFactory
+ );
+
+ void appendExportFilters(
+ TSortedFilterList& _rFilterMatcher,
+ const css::uno::Reference< css::ui::dialogs::XFilterManager >& _rFilterManager,
+ OUString& /* [out] */ _rFirstNonEmpty,
+ FileDialogHelper_Impl& _rFileDlgImpl
+ );
+
+
+ /** adds the given filters to the filter manager.
+ <p>To be used when opening generic files.</p>
+ */
+ void appendFiltersForOpen(
+ TSortedFilterList& _rFilterMatcher,
+ const css::uno::Reference< css::ui::dialogs::XFilterManager >& _rFilterManager,
+ OUString& /* [out] */ _rFirstNonEmpty,
+ FileDialogHelper_Impl& _rFileDlgImpl
+ );
+
+
+ /** adds the given extension to the display text.
+ <p>To be used when opening or save generic files.</p>
+ */
+ OUString addExtension(
+ const OUString& _rDisplayText,
+ const OUString& _rExtension,
+ bool _bForOpen,
+ FileDialogHelper_Impl& _rFileDlgImpl
+ );
+
+
+} // namespace sfx2
+
+
+#endif // INCLUDED_SFX2_SOURCE_DIALOG_FILTERGROUPING_HXX
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/infobar.cxx b/sfx2/source/dialog/infobar.cxx
new file mode 100644
index 000000000..eade717ea
--- /dev/null
+++ b/sfx2/source/dialog/infobar.cxx
@@ -0,0 +1,524 @@
+/* -*- 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/.
+ */
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <memory>
+#include <officecfg/Office/UI/Infobar.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/image.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+#include <bitmaps.hlst>
+
+using namespace drawinglayer::geometry;
+using namespace drawinglayer::processor2d;
+using namespace drawinglayer::primitive2d;
+using namespace drawinglayer::attribute;
+using namespace basegfx;
+using namespace css::frame;
+
+namespace
+{
+void GetInfoBarColors(InfobarType ibType, BColor& rBackgroundColor, BColor& rForegroundColor,
+ BColor& rMessageColor)
+{
+ rMessageColor = basegfx::BColor(0.0, 0.0, 0.0);
+
+ switch (ibType)
+ {
+ case InfobarType::INFO: // blue; #004785/0,71,133; #BDE5F8/189,229,248
+ rBackgroundColor = basegfx::BColor(0.741, 0.898, 0.973);
+ rForegroundColor = basegfx::BColor(0.0, 0.278, 0.522);
+ break;
+ case InfobarType::SUCCESS: // green; #32550C/50,85,12; #DFF2BF/223,242,191
+ rBackgroundColor = basegfx::BColor(0.874, 0.949, 0.749);
+ rForegroundColor = basegfx::BColor(0.196, 0.333, 0.047);
+ break;
+ case InfobarType::WARNING: // orange; #704300/112,67,0; #FEEFB3/254,239,179
+ rBackgroundColor = basegfx::BColor(0.996, 0.937, 0.702);
+ rForegroundColor = basegfx::BColor(0.439, 0.263, 0.0);
+ break;
+ case InfobarType::DANGER: // red; #7A0006/122,0,6; #FFBABA/255,186,186
+ rBackgroundColor = basegfx::BColor(1.0, 0.729, 0.729);
+ rForegroundColor = basegfx::BColor(0.478, 0.0, 0.024);
+ break;
+ }
+
+ //remove this?
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ if (rSettings.GetHighContrastMode())
+ {
+ rBackgroundColor = rSettings.GetLightColor().getBColor();
+ rForegroundColor = rSettings.GetDialogTextColor().getBColor();
+ }
+}
+OUString GetInfoBarIconName(InfobarType ibType)
+{
+ OUString aRet;
+
+ switch (ibType)
+ {
+ case InfobarType::INFO:
+ aRet = "vcl/res/infobox.png";
+ break;
+ case InfobarType::SUCCESS:
+ aRet = "vcl/res/successbox.png";
+ break;
+ case InfobarType::WARNING:
+ aRet = "vcl/res/warningbox.png";
+ break;
+ case InfobarType::DANGER:
+ aRet = "vcl/res/errorbox.png";
+ break;
+ }
+
+ return aRet;
+}
+
+} // anonymous namespace
+
+void SfxInfoBarWindow::SetCloseButtonImage()
+{
+ Size aSize = Image(StockImage::Yes, CLOSEDOC).GetSizePixel();
+ aSize = Size(aSize.Width() * 1.5, aSize.Height() * 1.5);
+
+ ScopedVclPtr<VirtualDevice> xDevice(m_xCloseBtn->create_virtual_device());
+ xDevice->SetOutputSizePixel(aSize);
+
+ Point aBtnPos(0, 0);
+
+ const ViewInformation2D aNewViewInfos;
+ const std::unique_ptr<BaseProcessor2D> pProcessor(
+ createBaseProcessor2DFromOutputDevice(*xDevice, aNewViewInfos));
+
+ const ::tools::Rectangle aRect(aBtnPos, xDevice->PixelToLogic(aSize));
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(2);
+
+ // background
+ B2DPolygon aPolygon;
+ aPolygon.append(B2DPoint(aRect.Left(), aRect.Top()));
+ aPolygon.append(B2DPoint(aRect.Right(), aRect.Top()));
+ aPolygon.append(B2DPoint(aRect.Right(), aRect.Bottom()));
+ aPolygon.append(B2DPoint(aRect.Left(), aRect.Bottom()));
+ aPolygon.setClosed(true);
+
+ aSeq[0] = new PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), m_aBackgroundColor);
+
+ LineAttribute aLineAttribute(m_aForegroundColor, 2.0);
+
+ // Cross
+ B2DPolyPolygon aCross;
+
+ B2DPolygon aLine1;
+ aLine1.append(B2DPoint(aRect.Left(), aRect.Top()));
+ aLine1.append(B2DPoint(aRect.Right(), aRect.Bottom()));
+ aCross.append(aLine1);
+
+ B2DPolygon aLine2;
+ aLine2.append(B2DPoint(aRect.Right(), aRect.Top()));
+ aLine2.append(B2DPoint(aRect.Left(), aRect.Bottom()));
+ aCross.append(aLine2);
+
+ aSeq[1] = new PolyPolygonStrokePrimitive2D(aCross, aLineAttribute, StrokeAttribute());
+
+ pProcessor->process(aSeq);
+
+ m_xCloseBtn->set_item_image("close", xDevice);
+}
+
+class ExtraButton
+{
+private:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xContainer;
+ std::unique_ptr<weld::Button> m_xButton;
+ /** StatusListener. Updates the button as the slot state changes */
+ rtl::Reference<weld::WidgetStatusListener> m_xStatusListener;
+ OUString m_aCommand;
+
+ DECL_LINK(CommandHdl, weld::Button&, void);
+
+public:
+ ExtraButton(weld::Container* pContainer, const OUString* pCommand)
+ : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/extrabutton.ui"))
+ , m_xContainer(m_xBuilder->weld_container("ExtraButton"))
+ , m_xButton(m_xBuilder->weld_button("button"))
+ {
+ if (pCommand)
+ {
+ m_aCommand = *pCommand;
+ m_xButton->connect_clicked(LINK(this, ExtraButton, CommandHdl));
+ m_xStatusListener.set(new weld::WidgetStatusListener(m_xButton.get(), m_aCommand));
+ m_xStatusListener->startListening();
+ }
+ }
+
+ ~ExtraButton()
+ {
+ if (m_xStatusListener.is())
+ m_xStatusListener->dispose();
+ }
+
+ weld::Button& get_widget() { return *m_xButton; }
+};
+
+IMPL_LINK_NOARG(ExtraButton, CommandHdl, weld::Button&, void)
+{
+ comphelper::dispatchCommand(m_aCommand, css::uno::Sequence<css::beans::PropertyValue>());
+}
+
+SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window* pParent, const OUString& sId,
+ const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage, InfobarType ibType,
+ bool bShowCloseButton)
+ : InterimItemWindow(pParent, "sfx/ui/infobar.ui", "InfoBar")
+ , m_sId(sId)
+ , m_eType(ibType)
+ , m_bLayingOut(false)
+ , m_xImage(m_xBuilder->weld_image("image"))
+ , m_xPrimaryMessage(m_xBuilder->weld_label("primary"))
+ , m_xSecondaryMessage(m_xBuilder->weld_text_view("secondary"))
+ , m_xButtonBox(m_xBuilder->weld_container("buttonbox"))
+ , m_xCloseBtn(m_xBuilder->weld_toolbar("closebar"))
+{
+ SetStyle(GetStyle() | WB_DIALOGCONTROL);
+
+ InitControlBase(m_xCloseBtn.get());
+
+ m_xImage->set_from_icon_name(GetInfoBarIconName(ibType));
+ m_xSecondaryMessage->set_margin_top(m_xImage->get_preferred_size().Height() / 4);
+
+ if (!sPrimaryMessage.isEmpty())
+ {
+ m_xPrimaryMessage->set_label(sPrimaryMessage);
+ m_xPrimaryMessage->show();
+ }
+
+ m_xSecondaryMessage->set_text(sSecondaryMessage);
+ m_aOrigMessageSize = m_xSecondaryMessage->get_preferred_size();
+ m_aMessageSize = m_aOrigMessageSize;
+ m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl));
+
+ if (bShowCloseButton)
+ {
+ m_xCloseBtn->connect_clicked(LINK(this, SfxInfoBarWindow, CloseHandler));
+ m_xCloseBtn->show();
+ }
+
+ EnableChildTransparentMode();
+
+ SetForeAndBackgroundColors(m_eType);
+
+ auto nWidth = pParent->GetSizePixel().getWidth();
+ auto nHeight = get_preferred_size().Height();
+ SetSizePixel(Size(nWidth, nHeight + 2));
+
+ Resize();
+}
+
+IMPL_LINK(SfxInfoBarWindow, SizeAllocHdl, const Size&, rSize, void)
+{
+ if (m_aMessageSize != rSize)
+ {
+ m_aMessageSize = rSize;
+ static_cast<SfxInfoBarContainerWindow*>(GetParent())->TriggerUpdateLayout();
+ }
+}
+
+Size SfxInfoBarWindow::DoLayout()
+{
+ Size aGivenSize(GetSizePixel());
+
+ // disconnect SizeAllocHdl because we don't care about the size change
+ // during layout
+ m_xSecondaryMessage->connect_size_allocate(Link<const Size&, void>());
+
+ // blow away size cache in case m_aMessageSize.Width() is already the width request
+ // and we would get the cached preferred size instead of the recalc we want to force
+ m_xSecondaryMessage->set_size_request(-1, -1);
+ // make the width we were detected as set to by SizeAllocHdl as our desired width
+ m_xSecondaryMessage->set_size_request(m_aMessageSize.Width(), -1);
+ // get our preferred size with that message width
+ Size aSizeForWidth(aGivenSize.Width(), m_xContainer->get_preferred_size().Height());
+ // restore the message preferred size so we can freely resize, and get a new
+ // m_aMessageSize and repeat the process if we do
+ m_xSecondaryMessage->set_size_request(m_aOrigMessageSize.Width(), -1);
+
+ // connect SizeAllocHdl so changes outside of this layout will trigger a new layout
+ m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl));
+
+ return aSizeForWidth;
+}
+
+void SfxInfoBarWindow::Layout()
+{
+ if (m_bLayingOut)
+ return;
+ m_bLayingOut = true;
+
+ InterimItemWindow::Layout();
+
+ m_bLayingOut = false;
+}
+
+weld::Button& SfxInfoBarWindow::addButton(const OUString* pCommand)
+{
+ m_aActionBtns.emplace_back(std::make_unique<ExtraButton>(m_xButtonBox.get(), pCommand));
+
+ return m_aActionBtns.back()->get_widget();
+}
+
+SfxInfoBarWindow::~SfxInfoBarWindow() { disposeOnce(); }
+
+void SfxInfoBarWindow::SetForeAndBackgroundColors(InfobarType eType)
+{
+ basegfx::BColor aMessageColor;
+ GetInfoBarColors(eType, m_aBackgroundColor, m_aForegroundColor, aMessageColor);
+
+ m_xPrimaryMessage->set_font_color(Color(aMessageColor));
+ m_xSecondaryMessage->set_font_color(Color(aMessageColor));
+
+ Color aBackgroundColor(m_aBackgroundColor);
+ m_xPrimaryMessage->set_background(aBackgroundColor);
+ m_xSecondaryMessage->set_background(aBackgroundColor);
+ m_xContainer->set_background(aBackgroundColor);
+ if (m_xCloseBtn->get_visible())
+ {
+ m_xCloseBtn->set_background(aBackgroundColor);
+ SetCloseButtonImage();
+ }
+}
+
+void SfxInfoBarWindow::dispose()
+{
+ for (auto& rxBtn : m_aActionBtns)
+ rxBtn.reset();
+
+ m_xImage.reset();
+ m_xPrimaryMessage.reset();
+ m_xSecondaryMessage.reset();
+ m_xButtonBox.reset();
+ m_xCloseBtn.reset();
+ m_aActionBtns.clear();
+ InterimItemWindow::dispose();
+}
+
+void SfxInfoBarWindow::Update(const OUString& sPrimaryMessage, const OUString& sSecondaryMessage,
+ InfobarType eType)
+{
+ if (m_eType != eType)
+ {
+ m_eType = eType;
+ SetForeAndBackgroundColors(m_eType);
+ m_xImage->set_from_icon_name(GetInfoBarIconName(eType));
+ }
+
+ m_xPrimaryMessage->set_label(sPrimaryMessage);
+ m_xSecondaryMessage->set_text(sSecondaryMessage);
+ Resize();
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(SfxInfoBarWindow, CloseHandler, const OString&, void)
+{
+ static_cast<SfxInfoBarContainerWindow*>(GetParent())->removeInfoBar(this);
+}
+
+SfxInfoBarContainerWindow::SfxInfoBarContainerWindow(SfxInfoBarContainerChild* pChildWin)
+ : Window(pChildWin->GetParent(), WB_DIALOGCONTROL)
+ , m_pChildWin(pChildWin)
+ , m_aLayoutIdle("SfxInfoBarContainerWindow m_aLayoutIdle")
+ , m_bResizing(false)
+{
+ m_aLayoutIdle.SetPriority(TaskPriority::HIGHEST);
+ m_aLayoutIdle.SetInvokeHandler(LINK(this, SfxInfoBarContainerWindow, DoUpdateLayout));
+}
+
+IMPL_LINK_NOARG(SfxInfoBarContainerWindow, DoUpdateLayout, Timer*, void) { m_pChildWin->Update(); }
+
+SfxInfoBarContainerWindow::~SfxInfoBarContainerWindow() { disposeOnce(); }
+
+void SfxInfoBarContainerWindow::dispose()
+{
+ for (auto& infoBar : m_pInfoBars)
+ infoBar.disposeAndClear();
+ m_pInfoBars.clear();
+ Window::dispose();
+}
+
+VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId,
+ const OUString& sPrimaryMessage,
+ const OUString& sSecondaryMessage,
+ InfobarType ibType,
+ bool bShowCloseButton)
+{
+ if (!isInfobarEnabled(sId))
+ return nullptr;
+
+ auto pInfoBar = VclPtr<SfxInfoBarWindow>::Create(this, sId, sPrimaryMessage, sSecondaryMessage,
+ ibType, bShowCloseButton);
+
+ basegfx::BColor aBackgroundColor;
+ basegfx::BColor aForegroundColor;
+ basegfx::BColor aMessageColor;
+ GetInfoBarColors(ibType, aBackgroundColor, aForegroundColor, aMessageColor);
+ pInfoBar->m_aBackgroundColor = aBackgroundColor;
+ pInfoBar->m_aForegroundColor = aForegroundColor;
+ m_pInfoBars.push_back(pInfoBar);
+
+ Resize();
+ return pInfoBar;
+}
+
+VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::getInfoBar(std::u16string_view sId)
+{
+ for (auto const& infoBar : m_pInfoBars)
+ {
+ if (infoBar->getId() == sId)
+ return infoBar;
+ }
+ return nullptr;
+}
+
+bool SfxInfoBarContainerWindow::hasInfoBarWithID(std::u16string_view sId)
+{
+ return (getInfoBar(sId) != nullptr);
+}
+
+void SfxInfoBarContainerWindow::removeInfoBar(VclPtr<SfxInfoBarWindow> const& pInfoBar)
+{
+ // Remove
+ auto it = std::find(m_pInfoBars.begin(), m_pInfoBars.end(), pInfoBar);
+ if (it != m_pInfoBars.end())
+ {
+ it->disposeAndClear();
+ m_pInfoBars.erase(it);
+ }
+
+ m_pChildWin->Update();
+}
+
+bool SfxInfoBarContainerWindow::isInfobarEnabled(std::u16string_view sId)
+{
+ if (sId == u"readonly")
+ return officecfg::Office::UI::Infobar::Enabled::Readonly::get();
+ if (sId == u"signature")
+ return officecfg::Office::UI::Infobar::Enabled::Signature::get();
+ if (sId == u"donate")
+ return officecfg::Office::UI::Infobar::Enabled::Donate::get();
+ if (sId == u"getinvolved")
+ return officecfg::Office::UI::Infobar::Enabled::GetInvolved::get();
+ if (sId == u"hyphenationmissing")
+ return officecfg::Office::UI::Infobar::Enabled::HyphenationMissing::get();
+ if (sId == u"whatsnew")
+ return officecfg::Office::UI::Infobar::Enabled::WhatsNew::get();
+ if (sId == u"hiddentrackchanges")
+ return officecfg::Office::UI::Infobar::Enabled::HiddenTrackChanges::get();
+
+ return true;
+}
+
+// This triggers the SfxFrame to re-layout its childwindows
+void SfxInfoBarContainerWindow::TriggerUpdateLayout() { m_aLayoutIdle.Start(); }
+
+void SfxInfoBarContainerWindow::Resize()
+{
+ if (m_bResizing)
+ return;
+ m_bResizing = true;
+ const Size& rOrigSize = GetSizePixel();
+ auto nOrigWidth = rOrigSize.getWidth();
+ auto nOrigHeight = rOrigSize.getHeight();
+
+ tools::Long nHeight = 0;
+
+ for (auto& rxInfoBar : m_pInfoBars)
+ {
+ Size aOrigSize = rxInfoBar->GetSizePixel();
+ Size aSize(nOrigWidth, aOrigSize.Height());
+
+ Point aPos(0, nHeight);
+ // stage 1: provisionally size the infobar,
+ rxInfoBar->SetPosSizePixel(aPos, aSize);
+
+ // stage 2: perhaps allow height to stretch to fit
+ // the stage 1 width
+ aSize = rxInfoBar->DoLayout();
+ rxInfoBar->SetPosSizePixel(aPos, aSize);
+ rxInfoBar->Show();
+
+ // Stretch to fit the infobar(s)
+ nHeight += aSize.getHeight();
+ }
+
+ if (nOrigHeight != nHeight)
+ {
+ SetSizePixel(Size(nOrigWidth, nHeight));
+ TriggerUpdateLayout();
+ }
+
+ m_bResizing = false;
+}
+
+SFX_IMPL_POS_CHILDWINDOW_WITHID(SfxInfoBarContainerChild, SID_INFOBAR, SFX_OBJECTBAR_OBJECT);
+
+SfxInfoBarContainerChild::SfxInfoBarContainerChild(vcl::Window* _pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo*)
+ : SfxChildWindow(_pParent, nId)
+ , m_pBindings(pBindings)
+{
+ SetWindow(VclPtr<SfxInfoBarContainerWindow>::Create(this));
+ GetWindow()->SetPosSizePixel(Point(0, 0), Size(_pParent->GetSizePixel().getWidth(), 0));
+ GetWindow()->Show();
+
+ SetAlignment(SfxChildAlignment::LOWESTTOP);
+}
+
+SfxInfoBarContainerChild::~SfxInfoBarContainerChild() {}
+
+SfxChildWinInfo SfxInfoBarContainerChild::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ return aInfo;
+}
+
+void SfxInfoBarContainerChild::Update()
+{
+ // Layout to current width, this may change the height
+ if (vcl::Window* pChild = GetWindow())
+ {
+ Size aSize(pChild->GetSizePixel());
+ pChild->Resize();
+ if (aSize == pChild->GetSizePixel())
+ return;
+ }
+
+ // Refresh the frame to take the infobars container height change into account
+ const sal_uInt16 nId = GetChildWindowId();
+ SfxViewFrame* pVFrame = m_pBindings->GetDispatcher()->GetFrame();
+ pVFrame->ShowChildWindow(nId);
+
+ // Give the focus to the document view
+ pVFrame->GetWindow().GrabFocusToDocument();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/inputdlg.cxx b/sfx2/source/dialog/inputdlg.cxx
new file mode 100644
index 000000000..243f2fa09
--- /dev/null
+++ b/sfx2/source/dialog/inputdlg.cxx
@@ -0,0 +1,67 @@
+/* -*- 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/.
+ */
+
+#include <sfx2/inputdlg.hxx>
+
+InputDialog::InputDialog(weld::Widget* pParent, const OUString& rLabelText)
+ : GenericDialogController(pParent, "sfx/ui/inputdialog.ui", "InputDialog")
+ , m_xEntry(m_xBuilder->weld_entry("entry"))
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+{
+ m_xLabel->set_label(rLabelText);
+}
+
+void InputDialog::HideHelpBtn() { m_xHelp->hide(); }
+
+OUString InputDialog::GetEntryText() const { return m_xEntry->get_text(); }
+
+void InputDialog::SetEntryText(const OUString& rStr)
+{
+ m_xEntry->set_text(rStr);
+ m_xEntry->set_position(-1);
+}
+
+void InputDialog::SetEntryMessageType(weld::EntryMessageType aType)
+{
+ m_xEntry->set_message_type(aType);
+ if (aType == weld::EntryMessageType::Error)
+ {
+ m_xEntry->select_region(0, -1);
+ m_xEntry->grab_focus();
+ m_xOk->set_sensitive(false);
+ }
+ else
+ {
+ m_xOk->set_sensitive(true);
+ SetTooltip("");
+ }
+}
+
+void InputDialog::SetTooltip(const OUString& rStr)
+{
+ m_xEntry->set_tooltip_text(rStr);
+ m_xOk->set_tooltip_text(rStr);
+}
+
+void InputDialog::setCheckEntry(std::function<bool(OUString)> aFunc)
+{
+ mCheckEntry = aFunc;
+ m_xEntry->connect_changed(LINK(this, InputDialog, EntryChangedHdl));
+}
+
+IMPL_LINK_NOARG(InputDialog, EntryChangedHdl, weld::Entry&, void)
+{
+ if (mCheckEntry(m_xEntry->get_text()))
+ SetEntryMessageType(weld::EntryMessageType::Normal);
+ else
+ SetEntryMessageType(weld::EntryMessageType::Error);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/mailmodel.cxx b/sfx2/source/dialog/mailmodel.cxx
new file mode 100644
index 000000000..382a67736
--- /dev/null
+++ b/sfx2/source/dialog/mailmodel.cxx
@@ -0,0 +1,849 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/system/SimpleSystemMail.hpp>
+#include <com/sun/star/system/SimpleCommandMail.hpp>
+#include <com/sun/star/system/XSimpleMailClientSupplier.hpp>
+#include <com/sun/star/system/SimpleMailClientFlags.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/mailmodelapi.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+
+#include <unotools/tempfile.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/useroptions.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/string.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::system;
+
+namespace {
+
+// - class PrepareListener_Impl ------------------------------------------
+class PrepareListener_Impl : public ::cppu::WeakImplHelper< css::frame::XStatusListener >
+{
+ bool m_bState;
+public:
+ PrepareListener_Impl();
+
+ // css.frame.XStatusListener
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent) override;
+
+ // css.lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+
+ bool IsSet() const {return m_bState;}
+};
+
+}
+
+PrepareListener_Impl::PrepareListener_Impl() :
+ m_bState( false )
+{
+}
+
+void PrepareListener_Impl::statusChanged(const css::frame::FeatureStateEvent& rEvent)
+{
+ if( rEvent.IsEnabled )
+ rEvent.State >>= m_bState;
+ else
+ m_bState = false;
+}
+
+void PrepareListener_Impl::disposing(const css::lang::EventObject& /*rEvent*/)
+{
+}
+
+// class SfxMailModel -----------------------------------------------
+
+const char16_t PDF_DOCUMENT_TYPE[] = u"pdf_Portable_Document_Format";
+
+SfxMailModel::SaveResult SfxMailModel::ShowFilterOptionsDialog(
+ const uno::Reference< lang::XMultiServiceFactory >& xSMGR,
+ const uno::Reference< frame::XModel >& xModel,
+ const OUString& rFilterName,
+ std::u16string_view rType,
+ bool bModified,
+ sal_Int32& rNumArgs,
+ css::uno::Sequence< css::beans::PropertyValue >& rArgs )
+{
+ SaveResult eRet( SAVE_ERROR );
+
+ try
+ {
+ uno::Sequence < beans::PropertyValue > aProps;
+ css::uno::Reference< css::container::XNameAccess > xFilterCFG(
+ xSMGR->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY );
+ css::uno::Reference< css::util::XModifiable > xModifiable( xModel, css::uno::UNO_QUERY );
+
+ if ( !xFilterCFG.is() )
+ return eRet;
+
+ uno::Any aAny = xFilterCFG->getByName( rFilterName );
+
+ if ( aAny >>= aProps )
+ {
+ for( const auto& rProp : std::as_const(aProps) )
+ {
+ if( rProp.Name == "UIComponent" )
+ {
+ OUString aServiceName;
+ rProp.Value >>= aServiceName;
+ if( !aServiceName.isEmpty() )
+ {
+ uno::Reference< ui::dialogs::XExecutableDialog > xFilterDialog(
+ xSMGR->createInstance( aServiceName ), uno::UNO_QUERY );
+ uno::Reference< beans::XPropertyAccess > xFilterProperties(
+ xFilterDialog, uno::UNO_QUERY );
+
+ if( xFilterDialog.is() && xFilterProperties.is() )
+ {
+ uno::Reference< document::XExporter > xExporter( xFilterDialog, uno::UNO_QUERY );
+
+ if ( rType == PDF_DOCUMENT_TYPE )
+ {
+ //add an internal property, used to tell the dialog we want to set a different
+ //string for the ok button
+ //used in filter/source/pdf/impdialog.cxx
+ uno::Sequence< beans::PropertyValue > aFilterDataValue{
+ comphelper::makePropertyValue("_OkButtonString",
+ SfxResId(STR_PDF_EXPORT_SEND ))
+ };
+
+ //add to the filterdata property, the only one the PDF export filter dialog will care for
+ uno::Sequence< beans::PropertyValue > aPropsForDialog{
+ comphelper::makePropertyValue("FilterData", aFilterDataValue)
+ };
+
+ //when executing the dialog will merge the persistent FilterData properties
+ xFilterProperties->setPropertyValues( aPropsForDialog );
+ }
+
+ if( xExporter.is() )
+ xExporter->setSourceDocument( xModel );
+
+ if( xFilterDialog->execute() )
+ {
+ //get the filter data
+ const uno::Sequence< beans::PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();
+
+ //add them to the args
+ auto pProp = std::find_if(aPropsFromDialog.begin(), aPropsFromDialog.end(),
+ [](const beans::PropertyValue& rDialogProp) { return rDialogProp.Name == "FilterData"; });
+ if (pProp != aPropsFromDialog.end())
+ {
+ //found the filterdata, add to the storing argument
+ rArgs.realloc( ++rNumArgs );
+ auto pArgs = rArgs.getArray();
+ pArgs[rNumArgs-1].Name = pProp->Name;
+ pArgs[rNumArgs-1].Value = pProp->Value;
+ }
+ eRet = SAVE_SUCCESSFUL;
+ }
+ else
+ {
+ // cancel from dialog, then do not send
+ // If the model is not modified, it could be modified by the dispatch calls.
+ // Therefore set back to modified = false. This should not hurt if we call
+ // on a non-modified model.
+ if ( !bModified )
+ {
+ try
+ {
+ xModifiable->setModified( false );
+ }
+ catch( css::beans::PropertyVetoException& )
+ {
+ }
+ }
+ eRet = SAVE_CANCELLED;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return eRet;
+}
+
+bool SfxMailModel::IsEmpty() const
+{
+ return maAttachedDocuments.empty();
+}
+
+SfxMailModel::SaveResult SfxMailModel::SaveDocumentAsFormat(
+ const OUString& aSaveFileName,
+ const css::uno::Reference< css::uno::XInterface >& xFrameOrModel,
+ const OUString& rType,
+ OUString& rFileNamePath )
+{
+ SaveResult eRet( SAVE_ERROR );
+ bool bSendAsPDF = ( rType == PDF_DOCUMENT_TYPE );
+
+ css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = ::comphelper::getProcessServiceFactory();
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ if (!xContext.is())
+ return eRet;
+
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager( css::frame::ModuleManager::create(xContext) );
+
+ OUString aModule;
+ try
+ {
+ aModule = xModuleManager->identify( xFrameOrModel );
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+
+ css::uno::Reference< css::frame::XFrame > xFrame( xFrameOrModel, css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XModel > xModel( xFrameOrModel, css::uno::UNO_QUERY );
+ if ( xFrame.is() )
+ {
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+
+ // We need at least a valid module name and model reference
+ if ( !aModule.isEmpty() && xModel.is() )
+ {
+ bool bModified( false );
+ bool bHasLocation( false );
+ bool bStoreTo( false );
+
+ css::uno::Reference< css::util::XModifiable > xModifiable( xModel, css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XStorable > xStorable( xModel, css::uno::UNO_QUERY );
+
+ if ( xModifiable.is() )
+ bModified = xModifiable->isModified();
+ if ( xStorable.is() )
+ {
+ OUString aLocation = xStorable->getLocation();
+ INetURLObject aFileObj( aLocation );
+
+ bool bPrivateProtocol = ( aFileObj.GetProtocol() == INetProtocol::PrivSoffice );
+
+ bHasLocation = !aLocation.isEmpty() && !bPrivateProtocol;
+ OSL_ASSERT( !bPrivateProtocol );
+ }
+ if ( !rType.isEmpty() )
+ bStoreTo = true;
+
+ if ( xStorable.is() )
+ {
+ OUString aFilterName;
+ OUString aTypeName( rType );
+ OUString aFileName;
+ OUString aExtension;
+
+ css::uno::Reference< css::container::XContainerQuery > xContainerQuery(
+ xSMGR->createInstance( "com.sun.star.document.FilterFactory" ),
+ css::uno::UNO_QUERY );
+
+ if ( bStoreTo )
+ {
+ // Retrieve filter from type
+ css::uno::Sequence< css::beans::NamedValue > aQuery( bSendAsPDF ? 3 : 2 );
+ auto pQuery = aQuery.getArray();
+ pQuery[0].Name = "Type";
+ pQuery[0].Value <<= aTypeName;
+ pQuery[1].Name = "DocumentService";
+ pQuery[1].Value <<= aModule;
+ if( bSendAsPDF )
+ {
+ // #i91419#
+ // FIXME: we want just an export filter. However currently we need
+ // exact flag value as detailed in the filter configuration to get it
+ // this seems to be a bug
+ // without flags we get an import filter here, which is also unwanted
+ pQuery[2].Name = "Flags";
+ pQuery[2].Value <<= sal_Int32(0x80042); // SfxFilterFlags: EXPORT ALIEN 3RDPARTY
+ }
+
+ css::uno::Reference< css::container::XEnumeration > xEnumeration =
+ xContainerQuery->createSubSetEnumerationByProperties( aQuery );
+
+ if ( xEnumeration->hasMoreElements() )
+ {
+ ::comphelper::SequenceAsHashMap aFilterPropsHM( xEnumeration->nextElement() );
+ aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
+ "Name",
+ OUString() );
+ }
+
+ if ( bHasLocation )
+ {
+ // Retrieve filter from media descriptor
+ ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
+ OUString aOrgFilterName = aMediaDescrPropsHM.getUnpackedValueOrDefault(
+ "FilterName",
+ OUString() );
+ if ( aOrgFilterName == aFilterName )
+ {
+ // We should save the document in the original format. Therefore this
+ // is not a storeTo operation. To support signing in this case, reset
+ // bStoreTo flag.
+ bStoreTo = false;
+ }
+ }
+ }
+ else
+ {
+ if ( bHasLocation )
+ {
+ // Retrieve filter from media descriptor
+ ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
+ aFilterName = aMediaDescrPropsHM.getUnpackedValueOrDefault(
+ "FilterName",
+ OUString() );
+ }
+
+ if ( !bHasLocation || aFilterName.isEmpty())
+ {
+ // Retrieve the user defined default filter
+ try
+ {
+ ::comphelper::SequenceAsHashMap aFilterPropsHM( xModuleManager->getByName( aModule ) );
+ aFilterName = aFilterPropsHM.getUnpackedValueOrDefault(
+ "ooSetupFactoryDefaultFilter",
+ OUString() );
+ css::uno::Reference< css::container::XNameAccess > xNameAccess(
+ xContainerQuery, css::uno::UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ ::comphelper::SequenceAsHashMap aFilterPropsHM2( xNameAccess->getByName( aFilterName ) );
+ aTypeName = aFilterPropsHM2.getUnpackedValueOrDefault(
+ "Type",
+ OUString() );
+ }
+ }
+ catch ( css::container::NoSuchElementException& )
+ {
+ }
+ catch ( css::beans::UnknownPropertyException& )
+ {
+ }
+ }
+ }
+
+ // No filter found => error
+ // No type and no location => error
+ if (( aFilterName.isEmpty() ) ||
+ ( aTypeName.isEmpty() && !bHasLocation ))
+ return eRet;
+
+ // Determine file name and extension
+ if ( bHasLocation && !bStoreTo )
+ {
+ INetURLObject aFileObj( xStorable->getLocation() );
+ aExtension = aFileObj.getExtension();
+ }
+ else
+ {
+ css::uno::Reference< container::XNameAccess > xTypeDetection(
+ xSMGR->createInstance( "com.sun.star.document.TypeDetection" ),
+ css::uno::UNO_QUERY );
+
+
+ if ( xTypeDetection.is() )
+ {
+ try
+ {
+ ::comphelper::SequenceAsHashMap aTypeNamePropsHM( xTypeDetection->getByName( aTypeName ) );
+ uno::Sequence< OUString > aExtensions = aTypeNamePropsHM.getUnpackedValueOrDefault(
+ "Extensions",
+ ::uno::Sequence< OUString >() );
+ if ( aExtensions.hasElements() )
+ aExtension = aExtensions[0];
+ }
+ catch ( css::container::NoSuchElementException& )
+ {
+ }
+ }
+ }
+
+ // Use provided save file name. If empty determine file name
+ aFileName = aSaveFileName;
+ if ( aFileName.isEmpty() )
+ {
+ if ( !bHasLocation )
+ {
+ // Create a noname file name with the correct extension
+ aFileName = "noname";
+ }
+ else
+ {
+ // Determine file name from model
+ INetURLObject aFileObj( xStorable->getLocation() );
+ aFileName = aFileObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::NONE );
+ }
+ }
+
+ // No file name => error
+ if ( aFileName.isEmpty() )
+ return eRet;
+
+ OSL_ASSERT( !aFilterName.isEmpty() );
+ OSL_ASSERT( !aFileName.isEmpty() );
+
+ // Creates a temporary directory to store a predefined file into it.
+ // This makes it possible to store the file for "send document as e-mail"
+ // with the original file name. We cannot use the original file as
+ // some mail programs need exclusive access.
+ ::utl::TempFile aTempDir( nullptr, true );
+
+ INetURLObject aFilePathObj( aTempDir.GetURL() );
+ aFilePathObj.insertName( aFileName );
+ aFilePathObj.setExtension( aExtension );
+
+ OUString aFileURL = aFilePathObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ sal_Int32 nNumArgs(1);
+ static const OUStringLiteral aPasswordPropName( u"Password" );
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ "FilterName", aFilterName) };
+
+ ::comphelper::SequenceAsHashMap aMediaDescrPropsHM( xModel->getArgs() );
+ OUString aPassword = aMediaDescrPropsHM.getUnpackedValueOrDefault(
+ aPasswordPropName,
+ OUString() );
+ if ( !aPassword.isEmpty() )
+ {
+ aArgs.realloc( ++nNumArgs );
+ auto pArgs = aArgs.getArray();
+ pArgs[nNumArgs-1].Name = aPasswordPropName;
+ pArgs[nNumArgs-1].Value <<= aPassword;
+ }
+
+ bool bNeedsPreparation = false;
+ css::util::URL aPrepareURL;
+ css::uno::Reference< css::frame::XDispatch > xPrepareDispatch;
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( xFrame, css::uno::UNO_QUERY );
+ css::uno::Reference< css::util::XURLTransformer > xURLTransformer( css::util::URLTransformer::create( xContext ) );
+ if( !bSendAsPDF )
+ {
+ try
+ {
+ // check if the document needs to be prepared for sending as mail (embedding of links, removal of invisible content)
+
+ aPrepareURL.Complete = ".uno:PrepareMailExport";
+ xURLTransformer->parseStrict( aPrepareURL );
+
+ if ( xDispatchProvider.is() )
+ {
+ xPrepareDispatch.set( xDispatchProvider->queryDispatch( aPrepareURL, OUString(), 0 ));
+ if ( xPrepareDispatch.is() )
+ {
+ rtl::Reference<PrepareListener_Impl> pPrepareListener = new PrepareListener_Impl;
+ xPrepareDispatch->addStatusListener( pPrepareListener, aPrepareURL );
+ bNeedsPreparation = pPrepareListener->IsSet();
+ xPrepareDispatch->removeStatusListener( pPrepareListener, aPrepareURL );
+ }
+ }
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ if ( bModified || !bHasLocation || bStoreTo || bNeedsPreparation )
+ {
+ // Document is modified, is newly created or should be stored in a special format
+ try
+ {
+ if( bNeedsPreparation && xPrepareDispatch.is() )
+ {
+ try
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aDispatchArgs;
+ xPrepareDispatch->dispatch( aPrepareURL, aDispatchArgs );
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ //check if this is the pdf output filter (i#64555)
+ if( bSendAsPDF )
+ {
+ SaveResult eShowPDFFilterDialog = ShowFilterOptionsDialog(
+ xSMGR, xModel, aFilterName, rType, bModified, nNumArgs, aArgs );
+
+ // don't continue on dialog cancel or error
+ if ( eShowPDFFilterDialog != SAVE_SUCCESSFUL )
+ return eShowPDFFilterDialog;
+ }
+
+ xStorable->storeToURL( aFileURL, aArgs );
+ rFileNamePath = aFileURL;
+ eRet = SAVE_SUCCESSFUL;
+
+ if( !bSendAsPDF )
+ {
+ css::util::URL aURL;
+ // #i30432# notify that export is finished - the Writer may want to restore removed content
+ aURL.Complete = ".uno:MailExportFinished";
+ xURLTransformer->parseStrict( aURL );
+
+ if ( xDispatchProvider.is() )
+ {
+ css::uno::Reference< css::frame::XDispatch > xDispatch(
+ xDispatchProvider->queryDispatch( aURL, OUString(), 0 ));
+ if ( xDispatch.is() )
+ {
+ try
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aDispatchArgs;
+ xDispatch->dispatch( aURL, aDispatchArgs );
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+ // If the model is not modified, it could be modified by the dispatch calls.
+ // Therefore set back to modified = false. This should not hurt if we call
+ // on a non-modified model.
+ if ( !bModified )
+ {
+ try
+ {
+ xModifiable->setModified( false );
+ }
+ catch( css::beans::PropertyVetoException& )
+ {
+ }
+ }
+ }
+ catch ( css::io::IOException& )
+ {
+ eRet = SAVE_ERROR;
+ }
+ }
+ else
+ {
+ // We need 1:1 copy of the document to preserve an added signature.
+ aArgs.realloc( ++nNumArgs );
+ auto pArgs = aArgs.getArray();
+ pArgs[nNumArgs-1].Name = "CopyStreamIfPossible";
+ pArgs[nNumArgs-1].Value <<= true;
+
+ try
+ {
+ xStorable->storeToURL( aFileURL, aArgs );
+ rFileNamePath = aFileURL;
+ eRet = SAVE_SUCCESSFUL;
+ }
+ catch ( css::io::IOException& )
+ {
+ eRet = SAVE_ERROR;
+ }
+ }
+ }
+ }
+
+ return eRet;
+}
+
+SfxMailModel::SfxMailModel()
+{
+}
+
+SfxMailModel::~SfxMailModel()
+{
+}
+
+void SfxMailModel::AddToAddress( const OUString& rAddress )
+{
+ // don't add an empty address
+ if ( !rAddress.isEmpty() )
+ {
+ if ( !mpToList )
+ // create the list
+ mpToList.reset(new AddressList_Impl);
+
+ // add address to list
+ mpToList->push_back( rAddress );
+ }
+}
+
+SfxMailModel::SendMailResult SfxMailModel::AttachDocument(
+ const css::uno::Reference< css::uno::XInterface >& xFrameOrModel,
+ const OUString& sAttachmentTitle )
+{
+ OUString sFileName;
+
+ SaveResult eSaveResult = SaveDocumentAsFormat( sAttachmentTitle, xFrameOrModel, OUString()/*sDocumentType*/, sFileName );
+ if ( eSaveResult == SAVE_SUCCESSFUL && !sFileName.isEmpty() )
+ maAttachedDocuments.push_back(sFileName);
+ return eSaveResult == SAVE_SUCCESSFUL ? SEND_MAIL_OK : SEND_MAIL_ERROR;
+}
+
+SfxMailModel::SendMailResult SfxMailModel::Send( const css::uno::Reference< css::frame::XFrame >& xFrame )
+{
+ OSL_ENSURE(!maAttachedDocuments.empty(),"No document added!");
+ SendMailResult eResult = SEND_MAIL_ERROR;
+ if ( !maAttachedDocuments.empty() )
+ {
+ css::uno::Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ css::uno::Reference< XSimpleMailClientSupplier > xSimpleMailClientSupplier;
+
+ // Prefer the SimpleSystemMail service if available
+ try {
+ xSimpleMailClientSupplier = SimpleSystemMail::create( xContext );
+ }
+ catch ( const uno::Exception & )
+ {}
+
+ if ( ! xSimpleMailClientSupplier.is() )
+ {
+ try {
+ xSimpleMailClientSupplier = SimpleCommandMail::create( xContext );
+ }
+ catch ( const uno::Exception & )
+ {}
+ }
+
+ if ( xSimpleMailClientSupplier.is() )
+ {
+ css::uno::Reference< XSimpleMailClient > xSimpleMailClient = xSimpleMailClientSupplier->querySimpleMailClient();
+
+ if ( !xSimpleMailClient.is() )
+ {
+ // no mail client support => message box!
+ return SEND_MAIL_ERROR;
+ }
+
+ // we have a simple mail client
+ css::uno::Reference< XSimpleMailMessage > xSimpleMailMessage = xSimpleMailClient->createSimpleMailMessage();
+ if ( xSimpleMailMessage.is() )
+ {
+ sal_Int32 nSendFlags = SimpleMailClientFlags::DEFAULTS;
+ if ( maFromAddress.isEmpty() )
+ {
+ // from address not set, try figure out users e-mail address
+ CreateFromAddress_Impl( maFromAddress );
+ }
+ xSimpleMailMessage->setOriginator( maFromAddress );
+
+ size_t nToCount = mpToList ? mpToList->size() : 0;
+
+ // set recipient (only one) for this simple mail server!!
+ if ( nToCount >= 1 )
+ {
+ xSimpleMailMessage->setRecipient( mpToList->at( 0 ) );
+ nSendFlags = SimpleMailClientFlags::NO_USER_INTERFACE;
+ }
+
+ // all other recipient must be handled with CC recipients!
+ if ( nToCount > 1 )
+ {
+ Sequence< OUString > aCcRecipientSeq( nToCount - 1 );
+ std::copy_n(std::next(mpToList->begin()), aCcRecipientSeq.getLength(),
+ aCcRecipientSeq.getArray());
+ xSimpleMailMessage->setCcRecipient( aCcRecipientSeq );
+ }
+
+ Sequence< OUString > aAttachmentSeq(maAttachedDocuments.data(),maAttachedDocuments.size());
+
+ if ( xSimpleMailMessage->getSubject().isEmpty() ) {
+ INetURLObject url(
+ maAttachedDocuments[0], INetURLObject::EncodeMechanism::WasEncoded);
+ OUString subject(
+ url.getBase(
+ INetURLObject::LAST_SEGMENT, false,
+ INetURLObject::DecodeMechanism::WithCharset));
+ if (subject.isEmpty()) {
+ subject = maAttachedDocuments[0];
+ }
+ if ( maAttachedDocuments.size() > 1 )
+ subject += ", ...";
+ xSimpleMailMessage->setSubject( subject );
+ }
+ xSimpleMailMessage->setAttachement( aAttachmentSeq );
+
+ bool bSend( false );
+ try
+ {
+ xSimpleMailClient->sendSimpleMailMessage( xSimpleMailMessage, nSendFlags );
+ bSend = true;
+ }
+ catch ( IllegalArgumentException& )
+ {
+ }
+ catch ( Exception& )
+ {
+ }
+
+ if ( !bSend )
+ {
+ css::uno::Reference< css::awt::XWindow > xParentWindow = xFrame->getContainerWindow();
+
+ SolarMutexGuard aGuard;
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(Application::GetFrameWeld(xParentWindow), "sfx/ui/errorfindemaildialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorFindEmailDialog"));
+ xBox->run();
+ eResult = SEND_MAIL_CANCELLED;
+ }
+ else
+ eResult = SEND_MAIL_OK;
+ }
+ }
+ }
+ else
+ eResult = SEND_MAIL_CANCELLED;
+
+ return eResult;
+}
+
+SfxMailModel::SendMailResult SfxMailModel::SaveAndSend( const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& rTypeName )
+{
+ SaveResult eSaveResult;
+ SendMailResult eResult = SEND_MAIL_ERROR;
+ OUString aFileName;
+
+ eSaveResult = SaveDocumentAsFormat( OUString(), xFrame, rTypeName, aFileName );
+
+ if ( eSaveResult == SAVE_SUCCESSFUL )
+ {
+ maAttachedDocuments.push_back( aFileName );
+ return Send( xFrame );
+ }
+ else if ( eSaveResult == SAVE_CANCELLED )
+ eResult = SEND_MAIL_CANCELLED;
+
+ return eResult;
+}
+
+// functions -------------------------------------------------------------
+
+bool CreateFromAddress_Impl( OUString& rFrom )
+
+/* [Description]
+
+ This function tries to create a From-address with the help of IniManagers.
+ For this the fields 'first name', 'Name' and 'Email' are read from the
+ application-ini-data. If these fields are not set, FALSE is returned.
+
+ [Return value]
+
+ sal_True: Address could be created.
+ sal_False: Address could not be created.
+*/
+
+{
+ SvtUserOptions aUserCFG;
+ OUString aName = aUserCFG.GetLastName ();
+ OUString aFirstName = aUserCFG.GetFirstName ();
+ if ( !aFirstName.isEmpty() || !aName.isEmpty() )
+ {
+ if ( !aFirstName.isEmpty() )
+ {
+ rFrom = comphelper::string::strip(aFirstName, ' ');
+
+ if ( !aName.isEmpty() )
+ rFrom += " ";
+ }
+ rFrom += comphelper::string::strip(aName, ' ');
+ // remove illegal characters
+ rFrom = rFrom.replaceAll("<", "").replaceAll(">", "").replaceAll("@", "");
+ }
+ OUString aEmailName = aUserCFG.GetEmail();
+
+ // remove illegal characters
+ aEmailName = aEmailName.replaceAll("<", "").replaceAll(">", "");
+
+ if ( !aEmailName.isEmpty() )
+ {
+ if ( !rFrom.isEmpty() )
+ rFrom += " ";
+ rFrom += "<" + comphelper::string::strip(aEmailName, ' ') + ">";
+ }
+ else
+ rFrom.clear();
+ return !rFrom.isEmpty();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/mgetempl.cxx b/sfx2/source/dialog/mgetempl.cxx
new file mode 100644
index 000000000..3b683b743
--- /dev/null
+++ b/sfx2/source/dialog/mgetempl.cxx
@@ -0,0 +1,653 @@
+/* -*- 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 <comphelper/string.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/style.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/styfitem.hxx>
+#include <sfx2/styledlg.hxx>
+#include <sfx2/tabdlg.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/sfxsids.hrc>
+
+#include <sfx2/strings.hrc>
+
+#include <svl/stritem.hxx>
+#include <sfx2/dispatch.hxx>
+
+#include "mgetempl.hxx"
+
+/* SfxManageStyleSheetPage Constructor
+ *
+ * initializes the list box with the templates
+ */
+SfxManageStyleSheetPage::SfxManageStyleSheetPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet)
+ : SfxTabPage(pPage, pController, "sfx/ui/managestylepage.ui", "ManageStylePage", &rAttrSet)
+ , pStyle(&static_cast<SfxStyleDialogController*>(pController)->GetStyleSheet())
+ , pItem(nullptr)
+ , bModified(false)
+ , aName(pStyle->GetName())
+ , aFollow(pStyle->GetFollow())
+ , aParent(pStyle->GetParent())
+ , nFlags(pStyle->GetMask())
+ , m_xName(m_xBuilder->weld_entry("name"))
+ , m_xAutoCB(m_xBuilder->weld_check_button("autoupdate"))
+ , m_xFollowFt(m_xBuilder->weld_label("nextstyleft"))
+ , m_xFollowLb(m_xBuilder->weld_combo_box("nextstyle"))
+ , m_xEditStyleBtn(m_xBuilder->weld_button("editstyle"))
+ , m_xBaseFt(m_xBuilder->weld_label("linkedwithft"))
+ , m_xBaseLb(m_xBuilder->weld_combo_box("linkedwith"))
+ , m_xEditLinkStyleBtn(m_xBuilder->weld_button("editlinkstyle"))
+ , m_xFilterFt(m_xBuilder->weld_label("categoryft"))
+ , m_xFilterLb(m_xBuilder->weld_combo_box("category"))
+ , m_xDescFt(m_xBuilder->weld_label("desc"))
+ , m_xNameFt(m_xBuilder->weld_label("nameft"))
+{
+ m_xFollowLb->make_sorted();
+ // tdf#120188 like SwCharURLPage limit the width of the style combos
+ const int nMaxWidth(m_xFollowLb->get_approximate_digit_width() * 50);
+ m_xFollowLb->set_size_request(nMaxWidth , -1);
+ m_xBaseLb->make_sorted();
+ m_xBaseLb->set_size_request(nMaxWidth , -1);
+ //note that the code depends on categories not being lexically
+ //sorted, so if it's changed to sorted, the code needs to
+ //be adapted to be position unaware
+ m_xFilterLb->set_size_request(nMaxWidth , -1);
+
+ // this Page needs ExchangeSupport
+ SetExchangeSupport();
+
+ if ( aFollow.isEmpty() || aFollow == aName )
+ m_xEditStyleBtn->set_sensitive(false);
+ else
+ m_xEditStyleBtn->set_sensitive(true);
+
+ int linkSelectPos = m_xBaseLb->get_active();
+ if ( linkSelectPos == 0 )
+ m_xEditLinkStyleBtn->set_sensitive(false);
+ else
+ m_xEditLinkStyleBtn->set_sensitive(true);
+
+ mxFamilies = SfxApplication::GetModule_Impl()->CreateStyleFamilies();
+
+ SfxStyleSheetBasePool* pPool = nullptr;
+ SfxObjectShell* pDocShell = SfxObjectShell::Current();
+
+ if ( pDocShell )
+ pPool = pDocShell->GetStyleSheetPool();
+ OSL_ENSURE( pPool, "no Pool or no DocShell" );
+
+ if ( pPool )
+ {
+ pPool->First(pStyle->GetFamily()); // for SW - update internal list
+ }
+
+ if ( pStyle->GetName().isEmpty() && pPool )
+ {
+ // NullString as Name -> generate Name
+ OUString aNoName(SfxStyleDialogController::GenerateUnusedName(*pPool, pStyle->GetFamily()));
+ pStyle->SetName( aNoName );
+ aName = aNoName;
+ aFollow = pStyle->GetFollow();
+ aParent = pStyle->GetParent();
+ }
+ m_xName->set_text(pStyle->GetName());
+
+ // Set the field read-only if it is NOT an user-defined style
+ // but allow selecting and copying
+ if (pStyle->IsUserDefined())
+ {
+ m_xName->set_can_focus(true);
+ m_xName->set_editable(true);
+ m_xName->set_sensitive(true);
+ m_xName->grab_focus(); // tdf#142017 default to focus within the page, not in notebook tab
+ }
+ else
+ {
+ m_xName->set_sensitive(false);
+ }
+
+ if ( pStyle->HasFollowSupport() && pPool )
+ {
+ SfxStyleSheetBase* pPoolStyle = pPool->First(pStyle->GetFamily());
+
+ m_xFollowLb->freeze();
+
+ while ( pPoolStyle )
+ {
+ m_xFollowLb->append_text(pPoolStyle->GetName());
+ pPoolStyle = pPool->Next();
+ }
+
+ // A new Template is not yet in the Pool
+ if (m_xFollowLb->find_text(pStyle->GetName()) == -1)
+ m_xFollowLb->append_text(pStyle->GetName());
+
+ m_xFollowLb->thaw();
+ }
+ else
+ {
+ m_xFollowFt->set_sensitive(false);
+ m_xFollowFt->hide();
+ m_xFollowLb->set_sensitive(false);
+ m_xFollowLb->hide();
+ m_xEditStyleBtn->hide();
+ }
+
+ if ( pStyle->HasParentSupport() && pPool )
+ {
+ m_xBaseLb->freeze();
+
+ if ( pStyle->HasClearParentSupport() )
+ // the base template can be set to NULL
+ m_xBaseLb->append_text(SfxResId(STR_NONE));
+
+ SfxStyleSheetBase* pPoolStyle = pPool->First(pStyle->GetFamily());
+
+ while ( pPoolStyle )
+ {
+ const OUString aStr( pPoolStyle->GetName() );
+ // own name as base template
+ if ( aStr != aName )
+ m_xBaseLb->append_text(aStr);
+ pPoolStyle = pPool->Next();
+ }
+
+ m_xBaseLb->thaw();
+ }
+ else
+ {
+ m_xBaseFt->set_sensitive(false);
+ m_xBaseLb->set_sensitive(false);
+ }
+
+ size_t nCount = mxFamilies->size();
+ size_t i;
+ for ( i = 0; i < nCount; ++i )
+ {
+ pItem = &(mxFamilies->at(i));
+
+ if ( pItem->GetFamily() == pStyle->GetFamily() )
+ break;
+ }
+
+ if ( i < nCount )
+ {
+ sal_uInt16 nStyleFilterIdx = 0xffff;
+ // Filter flags
+ const SfxStyleFilter& rList = pItem->GetFilterList();
+ nCount = rList.size();
+ sal_uInt16 nIdx = 0;
+ SfxStyleSearchBits nMask = pStyle->GetMask() & ~SfxStyleSearchBits::UserDefined;
+
+ if ( nMask == SfxStyleSearchBits::Auto ) // User Template?
+ nMask = pStyle->GetMask();
+
+ for ( i = 0; i < nCount; ++i )
+ {
+ const SfxFilterTuple& rTupel = rList[ i ];
+
+ if ( rTupel.nFlags != SfxStyleSearchBits::Auto &&
+ rTupel.nFlags != SfxStyleSearchBits::Used &&
+ rTupel.nFlags != SfxStyleSearchBits::AllVisible &&
+ rTupel.nFlags != SfxStyleSearchBits::All )
+ {
+ OUString sId(OUString::number(i));
+ m_xFilterLb->insert(nIdx, rTupel.aName, &sId, nullptr, nullptr);
+ if ( ( rTupel.nFlags & nMask ) == nMask )
+ nStyleFilterIdx = nIdx;
+ ++nIdx;
+ }
+ }
+
+ if ( nStyleFilterIdx != 0xFFFF )
+ m_xFilterLb->set_active(nStyleFilterIdx);
+ }
+
+ if ( !m_xFilterLb->get_count() || !pStyle->IsUserDefined() )
+ {
+ pItem = nullptr;
+ m_xFilterFt->set_sensitive(false);
+ m_xFilterLb->set_sensitive(false);
+ }
+ else
+ m_xFilterLb->save_value();
+ SetDescriptionText_Impl();
+
+ if (m_xFollowLb->get_sensitive() || m_xBaseLb->get_sensitive())
+ {
+ m_xName->connect_focus_in(
+ LINK( this, SfxManageStyleSheetPage, GetFocusHdl ) );
+ m_xName->connect_focus_out(
+ LINK( this, SfxManageStyleSheetPage, LoseFocusHdl ) );
+ }
+ // It is a style with auto update? (SW only)
+ if(SfxItemState::SET == rAttrSet.GetItemState(SID_ATTR_AUTO_STYLE_UPDATE))
+ m_xAutoCB->show();
+ m_xFollowLb->connect_changed(LINK(this, SfxManageStyleSheetPage, EditStyleSelectHdl_Impl));
+ m_xBaseLb->connect_changed(LINK(this, SfxManageStyleSheetPage, EditLinkStyleSelectHdl_Impl));
+ m_xEditStyleBtn->connect_clicked(LINK(this, SfxManageStyleSheetPage, EditStyleHdl_Impl));
+ m_xEditLinkStyleBtn->connect_clicked(LINK(this, SfxManageStyleSheetPage, EditLinkStyleHdl_Impl));
+}
+
+SfxManageStyleSheetPage::~SfxManageStyleSheetPage()
+{
+ mxFamilies.reset();
+ pItem = nullptr;
+ pStyle = nullptr;
+}
+
+void SfxManageStyleSheetPage::UpdateName_Impl( weld::ComboBox* pBox,
+ const OUString& rNew )
+
+/* [Description]
+
+ After the change of a template name update the ListBox pBox
+
+ [Parameter]
+
+ ListBox* pBox ListBox, whose entries are to be updated
+ const String& rNew the new Name
+*/
+
+{
+ if (pBox->get_sensitive())
+ {
+ // it is the current entry, which name was modified
+ const bool bSelect = pBox->get_active_text() == aBuf;
+ int nOldIndex = pBox->find_text(aBuf);
+ if (nOldIndex != -1)
+ pBox->remove(nOldIndex);
+ pBox->append_text(rNew);
+
+ if (bSelect)
+ pBox->set_active_text(rNew);
+ }
+}
+
+void SfxManageStyleSheetPage::SetDescriptionText_Impl()
+
+/* [Description]
+
+ Set attribute description. Get the set metric for this.
+*/
+
+{
+ MapUnit eUnit = MapUnit::MapCM;
+ FieldUnit eFieldUnit( FieldUnit::CM );
+ SfxModule* pModule = SfxModule::GetActiveModule();
+ if ( pModule )
+ {
+ const SfxPoolItem* pPoolItem = pModule->GetItem( SID_ATTR_METRIC );
+ if ( pPoolItem )
+ eFieldUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>( pPoolItem )->GetValue());
+ }
+
+ switch ( eFieldUnit )
+ {
+ case FieldUnit::MM: eUnit = MapUnit::MapMM; break;
+ case FieldUnit::CM:
+ case FieldUnit::M:
+ case FieldUnit::KM: eUnit = MapUnit::MapCM; break;
+ case FieldUnit::POINT:
+ case FieldUnit::PICA: eUnit = MapUnit::MapPoint; break;
+ case FieldUnit::INCH:
+ case FieldUnit::FOOT:
+ case FieldUnit::MILE: eUnit = MapUnit::MapInch; break;
+
+ default:
+ OSL_FAIL( "non supported field unit" );
+ }
+ m_xDescFt->set_label(pStyle->GetDescription(eUnit));
+}
+
+IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditStyleSelectHdl_Impl, weld::ComboBox&, void)
+{
+ OUString aTemplName(m_xFollowLb->get_active_text());
+ OUString aEditTemplName(m_xName->get_text());
+ m_xEditStyleBtn->set_sensitive(aTemplName != aEditTemplName);
+}
+
+IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditStyleHdl_Impl, weld::Button&, void)
+{
+ OUString aTemplName(m_xFollowLb->get_active_text());
+ Execute_Impl(SID_STYLE_EDIT, aTemplName, static_cast<sal_uInt16>(pStyle->GetFamily()));
+}
+
+IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditLinkStyleSelectHdl_Impl, weld::ComboBox&, void)
+{
+ int linkSelectPos = m_xBaseLb->get_active();
+ if ( linkSelectPos == 0 )
+ m_xEditLinkStyleBtn->set_sensitive(false);
+ else
+ m_xEditLinkStyleBtn->set_sensitive(true);
+}
+
+IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditLinkStyleHdl_Impl, weld::Button&, void)
+{
+ OUString aTemplName(m_xBaseLb->get_active_text());
+ if (aTemplName != SfxResId(STR_NONE))
+ Execute_Impl( SID_STYLE_EDIT, aTemplName, static_cast<sal_uInt16>(pStyle->GetFamily()) );
+}
+
+// Internal: Perform functions through the Dispatcher
+bool SfxManageStyleSheetPage::Execute_Impl(
+ sal_uInt16 nId, const OUString &rStr, sal_uInt16 nFamily)
+{
+
+ SfxDispatcher &rDispatcher = *SfxGetpApp()->GetDispatcher_Impl();
+ SfxStringItem aItem(nId, rStr);
+ SfxUInt16Item aFamily(SID_STYLE_FAMILY, nFamily);
+ const SfxPoolItem* pItems[ 6 ];
+ sal_uInt16 nCount = 0;
+ if( !rStr.isEmpty() )
+ pItems[ nCount++ ] = &aItem;
+ pItems[ nCount++ ] = &aFamily;
+
+ pItems[ nCount++ ] = nullptr;
+
+ const SfxPoolItem* pItem = rDispatcher.Execute(
+ nId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ pItems );
+
+ return pItem != nullptr;
+
+}
+
+IMPL_LINK(SfxManageStyleSheetPage, GetFocusHdl, weld::Widget&, rControl, void)
+
+/* [Description]
+
+ StarView Handler; GetFocus-Handler of the Edits with the template name.
+*/
+
+{
+ weld::Entry& rEdit = dynamic_cast<weld::Entry&>(rControl);
+ aBuf = comphelper::string::stripStart(rEdit.get_text(), ' ');
+}
+
+IMPL_LINK(SfxManageStyleSheetPage, LoseFocusHdl, weld::Widget&, rControl, void)
+
+/* [Description]
+
+ StarView Handler; lose-focus-handler of the edits of the template name.
+ This will update the listbox with the subsequent templates. The current
+ template itself is not returned in the listbox of the base templates.
+*/
+
+{
+ weld::Entry& rEdit = dynamic_cast<weld::Entry&>(rControl);
+ const OUString aStr(comphelper::string::stripStart(rEdit.get_text(), ' '));
+ rEdit.set_text(aStr);
+ // Update the Listbox of the base template if possible
+ if ( aStr != aBuf )
+ UpdateName_Impl(m_xFollowLb.get(), aStr);
+}
+
+bool SfxManageStyleSheetPage::FillItemSet( SfxItemSet* rSet )
+
+/* [Description]
+
+ Handler for setting the (modified) data. I called from the OK of the
+ SfxTabDialog.
+
+ [Parameter]
+
+ SfxItemSet &rAttrSet The set, which receives the data.
+
+ [Return value]
+
+ sal_Bool sal_True: The data had been changed
+ sal_False: The data had not been changed
+
+ [Cross-reference]
+
+ <class SfxTabDialog>
+*/
+
+{
+ const int nFilterIdx = m_xFilterLb->get_active();
+
+ // Set Filter
+
+ if ( nFilterIdx != -1 &&
+ m_xFilterLb->get_value_changed_from_saved() &&
+ m_xFilterLb->get_sensitive() )
+ {
+ bModified = true;
+ OSL_ENSURE( pItem, "No Item" );
+ // is only possibly for user templates
+ SfxStyleSearchBits nMask = pItem->GetFilterList()[m_xFilterLb->get_id(nFilterIdx).toUInt32()].nFlags | SfxStyleSearchBits::UserDefined;
+ pStyle->SetMask( nMask );
+ }
+ if (m_xAutoCB->get_visible() && m_xAutoCB->get_state_changed_from_saved())
+ {
+ rSet->Put(SfxBoolItem(SID_ATTR_AUTO_STYLE_UPDATE, m_xAutoCB->get_active()));
+ }
+
+ return bModified;
+}
+
+
+void SfxManageStyleSheetPage::Reset( const SfxItemSet* /*rAttrSet*/ )
+
+/* [Description]
+
+ Handler to initialize the page with the initial data.
+
+ [Parameter]
+
+ const SfxItemSet &rAttrSet The data set
+
+ [Cross-reference]
+
+ <class SfxTabDialog>
+*/
+
+{
+ bModified = false;
+ OUString sCmp( pStyle->GetName() );
+
+ if ( sCmp != aName )
+ pStyle->SetName( aName );
+ m_xName->set_text( aName );
+ if (m_xName->get_editable())
+ m_xName->select_region(0, -1);
+
+ if ( m_xFollowLb->get_sensitive() )
+ {
+ sCmp = pStyle->GetFollow();
+
+ if ( sCmp != aFollow )
+ pStyle->SetFollow( aFollow );
+
+ if ( aFollow.isEmpty() )
+ {
+ m_xFollowLb->set_active_text( aName );
+ m_xEditStyleBtn->set_sensitive( false );
+ }
+ else
+ m_xFollowLb->set_active_text( aFollow );
+ }
+
+ if (m_xBaseLb->get_sensitive())
+ {
+ sCmp = pStyle->GetParent();
+
+ if ( sCmp != aParent )
+ pStyle->SetParent( aParent );
+
+ if ( aParent.isEmpty() )
+ {
+ m_xBaseLb->set_active_text( SfxResId(STR_NONE) );
+ m_xEditLinkStyleBtn->set_sensitive( false );
+ }
+ else
+ m_xBaseLb->set_active_text( aParent );
+
+ if ( SfxResId(STR_STANDARD) == aName )
+ {
+ // the default template can not be linked
+ m_xBaseFt->set_sensitive(false);
+ m_xBaseLb->set_sensitive(false);
+ }
+ }
+ else
+ m_xEditLinkStyleBtn->set_sensitive( false );
+
+ if (m_xFilterLb->get_sensitive())
+ {
+ SfxStyleSearchBits nCmp = pStyle->GetMask();
+
+ if ( nCmp != nFlags )
+ pStyle->SetMask( nFlags );
+ m_xFilterLb->set_active_text(m_xFilterLb->get_saved_value());
+ }
+}
+
+std::unique_ptr<SfxTabPage> SfxManageStyleSheetPage::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet *rAttrSet )
+{
+ return std::make_unique<SfxManageStyleSheetPage>(pPage, pController, *rAttrSet);
+}
+
+void SfxManageStyleSheetPage::ActivatePage( const SfxItemSet& rSet)
+
+/* [Description]
+
+ ActivatePage handler of SfxTabDialog, is used for the update of the
+ descriptive text, since this might have changed through changes of data on
+ other pages.
+
+ [Parameter]
+
+ const SfxItemSet& the set for the exchange of data; is not used here.
+
+ [Cross-reference]
+
+ <SfxTabDialog::ActivatePage(const SfxItemSet &)>
+*/
+
+{
+ SetDescriptionText_Impl();
+
+ // It is a style with auto update? (SW only)
+ const SfxBoolItem* pPoolItem;
+
+ if ( (pPoolItem = rSet.GetItemIfSet( SID_ATTR_AUTO_STYLE_UPDATE, false )) )
+ m_xAutoCB->set_active(pPoolItem->GetValue());
+ m_xAutoCB->save_state();
+ m_xName->save_value();
+}
+
+DeactivateRC SfxManageStyleSheetPage::DeactivatePage( SfxItemSet* pItemSet )
+
+/* [Description]
+
+ DeactivatePage-handler of SfxTabDialog; data is set on the template, so
+ that the correct inheritance on the other pages of the dialog is made.
+ If an error occurs, leaving the page is prevented.
+ [Parameter]
+
+ SfxItemSet* the set for the exchange of data; is not used here.
+
+ [Cross-reference]
+
+ <SfxTabDialog::DeactivatePage(SfxItemSet*)>
+*/
+
+{
+ DeactivateRC nRet = DeactivateRC::LeavePage;
+
+ if (m_xName->get_value_changed_from_saved())
+ {
+ // By pressing <Enter> LoseFocus() is not triggered through StarView
+ if (m_xName->has_focus())
+ LoseFocusHdl( *m_xName );
+
+ if (!pStyle->SetName(comphelper::string::stripStart(m_xName->get_text(), ' ')))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_TABPAGE_INVALIDNAME)));
+ xBox->run();
+ m_xName->grab_focus();
+ m_xName->select_region(0, -1);
+ return DeactivateRC::KeepPage;
+ }
+ bModified = true;
+ }
+
+ if (pStyle->HasFollowSupport() && m_xFollowLb->get_sensitive())
+ {
+ const OUString aFollowEntry( m_xFollowLb->get_active_text() );
+
+ if ( pStyle->GetFollow() != aFollowEntry )
+ {
+ if ( !pStyle->SetFollow( aFollowEntry ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_TABPAGE_INVALIDSTYLE)));
+ xBox->run();
+ m_xFollowLb->grab_focus();
+ return DeactivateRC::KeepPage;
+ }
+ bModified = true;
+ }
+ }
+
+ if (m_xBaseLb->get_sensitive())
+ {
+ OUString aParentEntry( m_xBaseLb->get_active_text() );
+
+ if ( SfxResId(STR_NONE) == aParentEntry || aParentEntry == pStyle->GetName() )
+ aParentEntry.clear();
+
+ if ( pStyle->GetParent() != aParentEntry )
+ {
+ if ( !pStyle->SetParent( aParentEntry ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_TABPAGE_INVALIDPARENT)));
+ xBox->run();
+ m_xBaseLb->grab_focus();
+ return DeactivateRC::KeepPage;
+ }
+ bModified = true;
+ nRet = nRet | DeactivateRC::RefreshSet;
+ }
+ }
+
+ if ( pItemSet )
+ FillItemSet( pItemSet );
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/mgetempl.hxx b/sfx2/source/dialog/mgetempl.hxx
new file mode 100644
index 000000000..ef0d2fdcd
--- /dev/null
+++ b/sfx2/source/dialog/mgetempl.hxx
@@ -0,0 +1,95 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SFX2_MGETEMPL_HXX
+#define INCLUDED_SFX2_MGETEMPL_HXX
+
+#include <sfx2/styfitem.hxx>
+#include <sfx2/tabdlg.hxx>
+#include <memory>
+#include <optional>
+
+namespace weld { class Button; }
+namespace weld { class CheckButton; }
+namespace weld { class ComboBox; }
+namespace weld { class Entry; }
+namespace weld { class Label; }
+namespace weld { class Widget; }
+
+/* expected:
+ SID_TEMPLATE_NAME : In: StringItem, Name of Template
+ SID_TEMPLATE_FAMILY : In: Family of Template
+*/
+
+class SfxManageStyleSheetPage final : public SfxTabPage
+{
+ SfxStyleSheetBase *pStyle;
+ std::optional<SfxStyleFamilies> mxFamilies;
+ const SfxStyleFamilyItem *pItem;
+ OUString aBuf;
+ bool bModified;
+
+ // initial data for the style
+ OUString aName;
+ OUString aFollow;
+ OUString aParent;
+ SfxStyleSearchBits nFlags;
+
+ std::unique_ptr<weld::Entry> m_xName;
+ std::unique_ptr<weld::CheckButton> m_xAutoCB;
+ std::unique_ptr<weld::Label> m_xFollowFt;
+ std::unique_ptr<weld::ComboBox> m_xFollowLb;
+ std::unique_ptr<weld::Button> m_xEditStyleBtn;
+ std::unique_ptr<weld::Label> m_xBaseFt;
+ std::unique_ptr<weld::ComboBox> m_xBaseLb;
+ std::unique_ptr<weld::Button> m_xEditLinkStyleBtn;
+ std::unique_ptr<weld::Label> m_xFilterFt;
+ std::unique_ptr<weld::ComboBox> m_xFilterLb;
+ std::unique_ptr<weld::Label> m_xDescFt;
+ std::unique_ptr<weld::Label> m_xNameFt;
+
+ friend class SfxStyleDialogController;
+
+ DECL_LINK(GetFocusHdl, weld::Widget&, void);
+ DECL_LINK(LoseFocusHdl, weld::Widget&, void);
+ DECL_LINK(EditStyleSelectHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(EditStyleHdl_Impl, weld::Button&, void);
+ DECL_LINK(EditLinkStyleSelectHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(EditLinkStyleHdl_Impl, weld::Button&, void);
+
+ void UpdateName_Impl(weld::ComboBox*, const OUString &rNew);
+ void SetDescriptionText_Impl();
+
+
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* );
+
+ virtual bool FillItemSet(SfxItemSet *) override;
+ virtual void Reset(const SfxItemSet *) override;
+
+ static bool Execute_Impl( sal_uInt16 nId, const OUString& rStr, sal_uInt16 nFamily );
+ virtual void ActivatePage(const SfxItemSet &) override;
+ virtual DeactivateRC DeactivatePage(SfxItemSet *) override;
+
+public:
+ SfxManageStyleSheetPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rAttrSet);
+ virtual ~SfxManageStyleSheetPage() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/navigat.cxx b/sfx2/source/dialog/navigat.cxx
new file mode 100644
index 000000000..ff9f8a9f7
--- /dev/null
+++ b/sfx2/source/dialog/navigat.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/navigat.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <helpids.h>
+
+SfxNavigatorWrapper::SfxNavigatorWrapper(vcl::Window* pParentWnd, sal_uInt16 nId)
+ : SfxChildWindow(pParentWnd , nId)
+{
+}
+
+void SfxNavigatorWrapper::Initialize()
+{
+ SetHideNotDelete(true);
+}
+
+SfxNavigator::SfxNavigator(SfxBindings* pBind ,
+ SfxChildWindow* pChildWin ,
+ vcl::Window* pParent,
+ SfxChildWinInfo* pInfo)
+ : SfxDockingWindow(pBind ,
+ pChildWin ,
+ pParent ,
+ "Navigator", "sfx/ui/navigator.ui")
+{
+ SetText(SfxResId(STR_SID_NAVIGATOR));
+ SetHelpId(HID_NAVIGATOR_WINDOW);
+ SetOutputSizePixel(Size(270, 240));
+ Initialize(pInfo);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/newstyle.cxx b/sfx2/source/dialog/newstyle.cxx
new file mode 100644
index 000000000..bc6fc7bab
--- /dev/null
+++ b/sfx2/source/dialog/newstyle.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/style.hxx>
+
+#include <sfx2/newstyle.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+// Private methods ------------------------------------------------------
+
+IMPL_LINK_NOARG(SfxNewStyleDlg, OKClickHdl, weld::Button&, void)
+{
+ const OUString aName(m_xColBox->get_active_text());
+ SfxStyleSheetBase* pStyle = m_rPool.Find(aName, m_eSearchFamily);
+ if ( pStyle )
+ {
+ if ( !pStyle->IsUserDefined() )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(STR_POOL_STYLE_NAME)));
+ xBox->run();
+ return;
+ }
+
+ if (RET_YES == m_xQueryOverwriteBox->run())
+ m_xDialog->response(RET_OK);
+ }
+ else
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SfxNewStyleDlg, OKHdl, weld::TreeView&, bool)
+{
+ OKClickHdl(*m_xOKBtn);
+ return true;
+}
+
+IMPL_LINK(SfxNewStyleDlg, ModifyHdl, weld::ComboBox&, rBox, void)
+{
+ m_xOKBtn->set_sensitive(!rBox.get_active_text().replaceAll(" ", "").isEmpty());
+}
+
+SfxNewStyleDlg::SfxNewStyleDlg(weld::Widget* pParent, SfxStyleSheetBasePool& rInPool, SfxStyleFamily eFam)
+ : GenericDialogController(pParent, "sfx/ui/newstyle.ui", "CreateStyleDialog")
+ , m_rPool(rInPool)
+ , m_eSearchFamily(eFam)
+ , m_xColBox(m_xBuilder->weld_entry_tree_view("stylegrid", "stylename", "styles"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , m_xQueryOverwriteBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_QUERY_OVERWRITE)))
+{
+ m_xColBox->set_entry_width_chars(20);
+ m_xColBox->set_height_request_by_rows(8);
+
+ m_xOKBtn->connect_clicked(LINK(this, SfxNewStyleDlg, OKClickHdl));
+ m_xColBox->connect_changed(LINK(this, SfxNewStyleDlg, ModifyHdl));
+ m_xColBox->connect_row_activated(LINK(this, SfxNewStyleDlg, OKHdl));
+
+ auto xIter = m_rPool.CreateIterator(eFam, SfxStyleSearchBits::UserDefined);
+ SfxStyleSheetBase *pStyle = xIter->First();
+ while (pStyle)
+ {
+ m_xColBox->append_text(pStyle->GetName());
+ pStyle = xIter->Next();
+ }
+}
+
+SfxNewStyleDlg::~SfxNewStyleDlg()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/partwnd.cxx b/sfx2/source/dialog/partwnd.cxx
new file mode 100644
index 000000000..e387d2c5b
--- /dev/null
+++ b/sfx2/source/dialog/partwnd.cxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <vcl/event.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <partwnd.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/frame.hxx>
+
+
+// SfxPartChildWnd_Impl
+
+
+SFX_IMPL_DOCKINGWINDOW( SfxPartChildWnd_Impl, SID_BROWSER );
+
+SfxPartChildWnd_Impl::SfxPartChildWnd_Impl
+(
+ vcl::Window* pParentWnd,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo
+)
+ : SfxChildWindow( pParentWnd, nId )
+{
+ // Create Window
+ SetWindow(VclPtr<SfxPartDockWnd_Impl>::Create( pBindings, this, pParentWnd, WB_STDDOCKWIN | WB_CLIPCHILDREN | WB_SIZEABLE | WB_3DLOOK ));
+ SetAlignment(SfxChildAlignment::TOP);
+
+ assert(pInfo);
+ pInfo->nFlags |= SfxChildWindowFlags::FORCEDOCK;
+
+ static_cast<SfxDockingWindow*>(GetWindow())->SetFloatingSize( Size( 175, 175 ) );
+ GetWindow()->SetSizePixel( Size( 175, 175 ) );
+
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( pInfo );
+ SetHideNotDelete( true );
+}
+
+SfxPartChildWnd_Impl::~SfxPartChildWnd_Impl()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame = GetFrame();
+
+ // If xFrame=NULL release pMgr! Because this window lives longer then the manager!
+ // In these case we got a xFrame->dispose() call from outside ... and has release our
+ // frame reference in our own DisposingListener.
+ // But don't do it, if xFrame already exist. Then dispose() must come from inside ...
+ // and we need a valid pMgr for further operations ...
+
+ SfxPartDockWnd_Impl* pWin = static_cast<SfxPartDockWnd_Impl*>(GetWindow());
+
+ if ( pWin && xFrame == pWin->GetBindings().GetActiveFrame() )
+ pWin->GetBindings().SetActiveFrame( nullptr );
+}
+
+bool SfxPartChildWnd_Impl::QueryClose()
+{
+ return static_cast<SfxPartDockWnd_Impl*>(GetWindow())->QueryClose();
+}
+
+
+// SfxPartDockWnd_Impl
+
+
+SfxPartDockWnd_Impl::SfxPartDockWnd_Impl
+(
+ SfxBindings* pBind,
+ SfxChildWindow* pChildWin,
+ vcl::Window* pParent,
+ WinBits nBits
+)
+ : SfxDockingWindow( pBind, pChildWin, pParent, nBits )
+{
+ css::uno::Reference < css::frame::XFrame2 > xFrame = css::frame::Frame::create(
+ ::comphelper::getProcessComponentContext() );
+ xFrame->initialize( VCLUnoHelper::GetInterface ( this ) );
+
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xLMPropSet( xFrame->getLayoutManager(), css::uno::UNO_QUERY_THROW );
+
+ xLMPropSet->setPropertyValue( "AutomaticToolbars", css::uno::Any( false ));
+ }
+ catch( css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+
+ pChildWin->SetFrame( css::uno::Reference<css::frame::XFrame>(xFrame,css::uno::UNO_QUERY_THROW) );
+ if ( pBind->GetDispatcher() )
+ {
+ css::uno::Reference < css::frame::XFramesSupplier >
+ xSupp ( pBind->GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY );
+ if ( xSupp.is() )
+ xSupp->getFrames()->append( css::uno::Reference<css::frame::XFrame>(xFrame, css::uno::UNO_QUERY_THROW) );
+ }
+ else {
+ OSL_FAIL("Bindings without Dispatcher!");
+ }
+}
+
+
+bool SfxPartDockWnd_Impl::QueryClose()
+{
+ bool bClose = true;
+ SfxChildWindow* pChild = GetChildWindow_Impl();
+ if( pChild )
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame = pChild->GetFrame();
+ if( xFrame.is() )
+ {
+ css::uno::Reference< css::frame::XController > xCtrl = xFrame->getController();
+ if( xCtrl.is() )
+ bClose = xCtrl->suspend( true );
+ }
+ }
+
+ return bClose;
+}
+
+
+bool SfxPartDockWnd_Impl::EventNotify( NotifyEvent& rEvt )
+{
+ if( rEvt.GetType() == MouseNotifyEvent::GETFOCUS )
+ {
+ SfxChildWindow* pChild = GetChildWindow_Impl();
+ if( pChild )
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame = pChild->GetFrame();
+ if( xFrame.is() )
+ xFrame->activate();
+ }
+ }
+
+ return SfxDockingWindow::EventNotify( rEvt );
+}
+
+void SfxPartDockWnd_Impl::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ SfxDockingWindow::FillInfo( rInfo );
+ rInfo.bVisible = false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/passwd.cxx b/sfx2/source/dialog/passwd.cxx
new file mode 100644
index 000000000..13822c4a9
--- /dev/null
+++ b/sfx2/source/dialog/passwd.cxx
@@ -0,0 +1,209 @@
+/* -*- 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 <sfx2/passwd.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+#include <rtl/ustrbuf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+IMPL_LINK_NOARG(SfxPasswordDialog, EditModifyHdl, weld::Entry&, void)
+{
+ ModifyHdl();
+}
+
+void SfxPasswordDialog::ModifyHdl()
+{
+ bool bEnable = m_xPassword1ED->get_text().getLength() >= mnMinLen;
+ if (m_xPassword2ED->get_visible())
+ bEnable = (bEnable && (m_xPassword2ED->get_text().getLength() >= mnMinLen));
+ m_xOKBtn->set_sensitive(bEnable);
+}
+
+IMPL_LINK(SfxPasswordDialog, InsertTextHdl, OUString&, rTest, bool)
+{
+ if (!mbAsciiOnly)
+ return true;
+
+ const sal_Unicode* pTest = rTest.getStr();
+ sal_Int32 nLen = rTest.getLength();
+ OUStringBuffer aFilter(nLen);
+ bool bReset = false;
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if( *pTest > 0x007f )
+ bReset = true;
+ else
+ aFilter.append(*pTest);
+ ++pTest;
+ }
+
+ if (bReset)
+ {
+ rTest = aFilter.makeStringAndClear();
+ // upgrade from "Normal" to "Warning" if a invalid letter was
+ // discarded
+ m_xOnlyAsciiFT->set_label_type(weld::LabelType::Warning);
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG(SfxPasswordDialog, OKHdl, weld::Button&, void)
+{
+ bool bConfirmFailed = bool( mnExtras & SfxShowExtras::CONFIRM ) &&
+ ( GetConfirm() != GetPassword() );
+ if( ( mnExtras & SfxShowExtras::CONFIRM2 ) && ( m_xConfirm2ED->get_text() != GetPassword2() ) )
+ bConfirmFailed = true;
+ if ( bConfirmFailed )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SfxResId(STR_ERROR_WRONG_CONFIRM)));
+ xBox->run();
+ m_xConfirm1ED->set_text(OUString());
+ m_xConfirm1ED->grab_focus();
+ }
+ else
+ m_xDialog->response(RET_OK);
+}
+
+// CTOR / DTOR -----------------------------------------------------------
+
+SfxPasswordDialog::SfxPasswordDialog(weld::Widget* pParent, const OUString* pGroupText)
+ : GenericDialogController(pParent, "sfx/ui/password.ui", "PasswordDialog")
+ , m_xPassword1Box(m_xBuilder->weld_frame("password1frame"))
+ , m_xUserFT(m_xBuilder->weld_label("userft"))
+ , m_xUserED(m_xBuilder->weld_entry("usered"))
+ , m_xPassword1FT(m_xBuilder->weld_label("pass1ft"))
+ , m_xPassword1ED(m_xBuilder->weld_entry("pass1ed"))
+ , m_xConfirm1FT(m_xBuilder->weld_label("confirm1ft"))
+ , m_xConfirm1ED(m_xBuilder->weld_entry("confirm1ed"))
+ , m_xPassword2Box(m_xBuilder->weld_frame("password2frame"))
+ , m_xPassword2FT(m_xBuilder->weld_label("pass2ft"))
+ , m_xPassword2ED(m_xBuilder->weld_entry("pass2ed"))
+ , m_xConfirm2FT(m_xBuilder->weld_label("confirm2ft"))
+ , m_xConfirm2ED(m_xBuilder->weld_entry("confirm2ed"))
+ , m_xMinLengthFT(m_xBuilder->weld_label("minlenft"))
+ , m_xOnlyAsciiFT(m_xBuilder->weld_label("onlyascii"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , maMinLenPwdStr(SfxResId(STR_PASSWD_MIN_LEN))
+ , maMinLenPwdStr1(SfxResId(STR_PASSWD_MIN_LEN1))
+ , maEmptyPwdStr(SfxResId(STR_PASSWD_EMPTY))
+ , mnMinLen(5)
+ , mnExtras(SfxShowExtras::NONE)
+ , mbAsciiOnly(false)
+{
+ Link<weld::Entry&,void> aLink = LINK(this, SfxPasswordDialog, EditModifyHdl);
+ m_xPassword1ED->connect_changed(aLink);
+ m_xPassword2ED->connect_changed(aLink);
+ Link<OUString&,bool> aLink2 = LINK(this, SfxPasswordDialog, InsertTextHdl);
+ m_xPassword1ED->connect_insert_text(aLink2);
+ m_xPassword2ED->connect_insert_text(aLink2);
+ m_xConfirm1ED->connect_insert_text(aLink2);
+ m_xConfirm2ED->connect_insert_text(aLink2);
+ m_xOKBtn->connect_clicked(LINK(this, SfxPasswordDialog, OKHdl));
+
+ if (pGroupText)
+ m_xPassword1Box->set_label(*pGroupText);
+
+ //set the text to the password length
+ SetPasswdText();
+}
+
+void SfxPasswordDialog::SetPasswdText( )
+{
+ //set the new string to the minimum password length
+ if (mnMinLen == 0)
+ m_xMinLengthFT->set_label(maEmptyPwdStr);
+ else
+ {
+ if( mnMinLen == 1 )
+ m_xMinLengthFT->set_label(maMinLenPwdStr1);
+ else
+ {
+ maMainPwdStr = maMinLenPwdStr;
+ maMainPwdStr = maMainPwdStr.replaceAll( "$(MINLEN)", OUString::number(static_cast<sal_Int32>(mnMinLen) ) );
+ m_xMinLengthFT->set_label(maMainPwdStr);
+ }
+ }
+}
+
+
+void SfxPasswordDialog::SetMinLen( sal_uInt16 nLen )
+{
+ mnMinLen = nLen;
+ SetPasswdText();
+ ModifyHdl();
+}
+
+void SfxPasswordDialog::ShowMinLengthText(bool bShow)
+{
+ m_xMinLengthFT->set_visible(bShow);
+}
+
+void SfxPasswordDialog::AllowAsciiOnly()
+{
+ mbAsciiOnly = true;
+ m_xOnlyAsciiFT->show();
+}
+
+short SfxPasswordDialog::run()
+{
+ m_xUserFT->hide();
+ m_xUserED->hide();
+ m_xConfirm1FT->hide();
+ m_xConfirm1ED->hide();
+ m_xPassword1FT->hide();
+ m_xPassword2Box->hide();
+ m_xPassword2FT->hide();
+ m_xPassword2ED->hide();
+ m_xPassword2FT->hide();
+ m_xConfirm2FT->hide();
+ m_xConfirm2ED->hide();
+
+ if (mnExtras != SfxShowExtras::NONE)
+ m_xPassword1FT->show();
+ if (mnExtras & SfxShowExtras::USER)
+ {
+ m_xUserFT->show();
+ m_xUserED->show();
+ }
+ if (mnExtras & SfxShowExtras::CONFIRM)
+ {
+ m_xConfirm1FT->show();
+ m_xConfirm1ED->show();
+ }
+ if (mnExtras & SfxShowExtras::PASSWORD2)
+ {
+ m_xPassword2Box->show();
+ m_xPassword2FT->show();
+ m_xPassword2ED->show();
+ }
+ if (mnExtras & SfxShowExtras::CONFIRM2)
+ {
+ m_xConfirm2FT->show();
+ m_xConfirm2ED->show();
+ }
+
+ return GenericDialogController::run();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/printopt.cxx b/sfx2/source/dialog/printopt.cxx
new file mode 100644
index 000000000..8d16edfc0
--- /dev/null
+++ b/sfx2/source/dialog/printopt.cxx
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/macros.h>
+#include <officecfg/Office/Common.hxx>
+#include <svtools/printoptions.hxx>
+#include <svtools/restartdialog.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <sfx2/printopt.hxx>
+
+static sal_uInt16 aDPIArray[] = { 72, 96, 150, 200, 300, 600 };
+static bool bOutputForPrinter = true;
+
+#define DPI_COUNT SAL_N_ELEMENTS(aDPIArray)
+
+SfxCommonPrintOptionsTabPage::SfxCommonPrintOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+ : SfxTabPage(pPage, pController, "sfx/ui/optprintpage.ui", "OptPrintPage", &rSet)
+ , m_xPrinterOutputRB(m_xBuilder->weld_radio_button("printer"))
+ , m_xPrintFileOutputRB(m_xBuilder->weld_radio_button("file"))
+ , m_xReduceTransparencyCB(m_xBuilder->weld_check_button("reducetrans"))
+ , m_xReduceTransparencyAutoRB(m_xBuilder->weld_radio_button("reducetransauto"))
+ , m_xReduceTransparencyNoneRB(m_xBuilder->weld_radio_button("reducetransnone"))
+ , m_xReduceGradientsCB(m_xBuilder->weld_check_button("reducegrad"))
+ , m_xReduceGradientsStripesRB(m_xBuilder->weld_radio_button("reducegradstripes"))
+ , m_xReduceGradientsColorRB(m_xBuilder->weld_radio_button("reducegradcolor"))
+ , m_xReduceGradientsStepCountNF(m_xBuilder->weld_spin_button("reducegradstep"))
+ , m_xReduceBitmapsCB(m_xBuilder->weld_check_button("reducebitmap"))
+ , m_xReduceBitmapsOptimalRB(m_xBuilder->weld_radio_button("reducebitmapoptimal"))
+ , m_xReduceBitmapsNormalRB(m_xBuilder->weld_radio_button("reducebitmapnormal"))
+ , m_xReduceBitmapsResolutionRB(m_xBuilder->weld_radio_button("reducebitmapresol"))
+ , m_xReduceBitmapsResolutionLB(m_xBuilder->weld_combo_box("reducebitmapdpi"))
+ , m_xReduceBitmapsTransparencyCB(m_xBuilder->weld_check_button("reducebitmaptrans"))
+ , m_xConvertToGreyscalesCB(m_xBuilder->weld_check_button("converttogray"))
+ , m_xPDFCB(m_xBuilder->weld_check_button("pdf"))
+ , m_xPaperSizeCB(m_xBuilder->weld_check_button("papersize"))
+ , m_xPaperOrientationCB(m_xBuilder->weld_check_button("paperorient"))
+ , m_xTransparencyCB(m_xBuilder->weld_check_button("trans"))
+{
+#ifndef ENABLE_CUPS
+ m_xPDFCB->hide();
+#endif
+
+ if( bOutputForPrinter )
+ {
+ m_xPrinterOutputRB->set_active(true);
+ }
+ else
+ {
+ m_xPrintFileOutputRB->set_active(true);
+ m_xPDFCB->set_sensitive(false);
+ }
+
+ m_xPrinterOutputRB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ToggleOutputPrinterRBHdl ) );
+ m_xPrintFileOutputRB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ToggleOutputPrintFileRBHdl ) );
+
+ m_xReduceTransparencyCB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ClickReduceTransparencyCBHdl ) );
+ m_xReduceGradientsCB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ClickReduceGradientsCBHdl ) );
+ m_xReduceBitmapsCB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ClickReduceBitmapsCBHdl ) );
+
+ m_xReduceGradientsStripesRB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ToggleReduceGradientsStripesRBHdl ) );
+ m_xReduceBitmapsResolutionRB->connect_toggled( LINK( this, SfxCommonPrintOptionsTabPage, ToggleReduceBitmapsResolutionRBHdl ) );
+}
+
+SfxCommonPrintOptionsTabPage::~SfxCommonPrintOptionsTabPage()
+{
+}
+
+std::unique_ptr<SfxTabPage> SfxCommonPrintOptionsTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet)
+{
+ return std::make_unique<SfxCommonPrintOptionsTabPage>(pPage, pController, *rAttrSet);
+}
+
+bool SfxCommonPrintOptionsTabPage::FillItemSet( SfxItemSet* /*rSet*/ )
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+
+ if( m_xPaperSizeCB->get_state_changed_from_saved())
+ officecfg::Office::Common::Print::Warning::PaperSize::set(m_xPaperSizeCB->get_active(), batch);
+ if( m_xPaperOrientationCB->get_state_changed_from_saved() )
+ officecfg::Office::Common::Print::Warning::PaperOrientation::set(m_xPaperOrientationCB->get_active(), batch);
+ if( m_xTransparencyCB->get_state_changed_from_saved() )
+ officecfg::Office::Common::Print::Warning::Transparency::set(m_xTransparencyCB->get_active(), batch);
+
+ batch->commit();
+
+ ImplSaveControls( m_xPrinterOutputRB->get_active() ? &maPrinterOptions : &maPrintFileOptions );
+
+ svtools::SetPrinterOptions(maPrinterOptions, /*bFile*/false);
+ svtools::SetPrinterOptions(maPrintFileOptions, /*bFile*/true);
+
+ return false;
+}
+
+void SfxCommonPrintOptionsTabPage::Reset( const SfxItemSet* /*rSet*/ )
+{
+ m_xPaperSizeCB->set_active(officecfg::Office::Common::Print::Warning::PaperSize::get());
+ m_xPaperOrientationCB->set_active(officecfg::Office::Common::Print::Warning::PaperOrientation::get());
+ m_xTransparencyCB->set_active(officecfg::Office::Common::Print::Warning::Transparency::get());
+
+ m_xPaperSizeCB->save_state();
+ m_xPaperOrientationCB->save_state();
+ m_xTransparencyCB->save_state();
+
+ svtools::GetPrinterOptions( maPrinterOptions, /*bFile*/false );
+ svtools::GetPrinterOptions( maPrintFileOptions, /*bFile*/true );
+ if(m_xPrintFileOutputRB->get_active()){
+ m_xPrinterOutputRB->set_active(true);
+ }
+
+ ImplUpdateControls( m_xPrinterOutputRB->get_active() ? &maPrinterOptions : &maPrintFileOptions );
+}
+
+DeactivateRC SfxCommonPrintOptionsTabPage::DeactivatePage( SfxItemSet* pItemSet )
+{
+ if( pItemSet )
+ FillItemSet( pItemSet );
+
+ return DeactivateRC::LeavePage;
+}
+
+void SfxCommonPrintOptionsTabPage::ImplUpdateControls( const vcl::printer::Options* pCurrentOptions )
+{
+ m_xReduceTransparencyCB->set_active( pCurrentOptions->IsReduceTransparency() );
+
+ if( pCurrentOptions->GetReducedTransparencyMode() == vcl::printer::TransparencyMode::Auto )
+ m_xReduceTransparencyAutoRB->set_active(true);
+ else
+ m_xReduceTransparencyNoneRB->set_active(true);
+
+ m_xReduceGradientsCB->set_active( pCurrentOptions->IsReduceGradients() );
+
+ if( pCurrentOptions->GetReducedGradientMode() == vcl::printer::GradientMode::Stripes )
+ m_xReduceGradientsStripesRB->set_active(true);
+ else
+ m_xReduceGradientsColorRB->set_active(true);
+
+ m_xReduceGradientsStepCountNF->set_value(pCurrentOptions->GetReducedGradientStepCount());
+
+ m_xReduceBitmapsCB->set_active( pCurrentOptions->IsReduceBitmaps() );
+
+ if( pCurrentOptions->GetReducedBitmapMode() == vcl::printer::BitmapMode::Optimal )
+ m_xReduceBitmapsOptimalRB->set_active(true);
+ else if( pCurrentOptions->GetReducedBitmapMode() == vcl::printer::BitmapMode::Normal )
+ m_xReduceBitmapsNormalRB->set_active(true);
+ else
+ m_xReduceBitmapsResolutionRB->set_active(true);
+
+ const sal_uInt16 nDPI = pCurrentOptions->GetReducedBitmapResolution();
+
+ if( nDPI < aDPIArray[ 0 ] )
+ m_xReduceBitmapsResolutionLB->set_active(0);
+ else
+ {
+ for( int i = DPI_COUNT - 1; i >= 0; i-- )
+ {
+ if( nDPI >= aDPIArray[ i ] )
+ {
+ m_xReduceBitmapsResolutionLB->set_active(i);
+ i = -1;
+ }
+ }
+ }
+
+ m_xReduceBitmapsTransparencyCB->set_active( pCurrentOptions->IsReducedBitmapIncludesTransparency() );
+ m_xConvertToGreyscalesCB->set_active( pCurrentOptions->IsConvertToGreyscales() );
+ m_xPDFCB->set_active( pCurrentOptions->IsPDFAsStandardPrintJobFormat() );
+
+ ClickReduceTransparencyCBHdl(*m_xReduceTransparencyCB);
+ ClickReduceGradientsCBHdl(*m_xReduceGradientsCB);
+ ClickReduceBitmapsCBHdl(*m_xReduceBitmapsCB);
+}
+
+void SfxCommonPrintOptionsTabPage::ImplSaveControls( vcl::printer::Options* pCurrentOptions )
+{
+ pCurrentOptions->SetReduceTransparency( m_xReduceTransparencyCB->get_active() );
+ pCurrentOptions->SetReducedTransparencyMode( m_xReduceTransparencyAutoRB->get_active() ? vcl::printer::TransparencyMode::Auto : vcl::printer::TransparencyMode::NONE );
+ pCurrentOptions->SetReduceGradients( m_xReduceGradientsCB->get_active() );
+ pCurrentOptions->SetReducedGradientMode( m_xReduceGradientsStripesRB->get_active() ? vcl::printer::GradientMode::Stripes : vcl::printer::GradientMode::Color );
+ pCurrentOptions->SetReducedGradientStepCount(m_xReduceGradientsStepCountNF->get_value());
+ pCurrentOptions->SetReduceBitmaps( m_xReduceBitmapsCB->get_active() );
+ pCurrentOptions->SetReducedBitmapMode( m_xReduceBitmapsOptimalRB->get_active() ? vcl::printer::BitmapMode::Optimal :
+ ( m_xReduceBitmapsNormalRB->get_active() ? vcl::printer::BitmapMode::Normal : vcl::printer::BitmapMode::Resolution ) );
+ pCurrentOptions->SetReducedBitmapResolution( aDPIArray[ std::min<sal_uInt16>( m_xReduceBitmapsResolutionLB->get_active(),
+ SAL_N_ELEMENTS(aDPIArray) - 1 ) ] );
+ pCurrentOptions->SetReducedBitmapIncludesTransparency( m_xReduceBitmapsTransparencyCB->get_active() );
+ pCurrentOptions->SetConvertToGreyscales( m_xConvertToGreyscalesCB->get_active() );
+ bool bOrigBackEnd = pCurrentOptions->IsPDFAsStandardPrintJobFormat();
+ if (bOrigBackEnd != m_xPDFCB->get_active())
+ {
+ pCurrentOptions->SetPDFAsStandardPrintJobFormat( m_xPDFCB->get_active() );
+ svtools::executeRestartDialog(
+ comphelper::getProcessComponentContext(), nullptr,
+ svtools::RESTART_REASON_PDF_AS_STANDARD_JOB_FORMAT);
+ }
+}
+
+IMPL_LINK_NOARG( SfxCommonPrintOptionsTabPage, ClickReduceTransparencyCBHdl, weld::Toggleable&, void )
+{
+ const bool bReduceTransparency = m_xReduceTransparencyCB->get_active();
+
+ m_xReduceTransparencyAutoRB->set_sensitive( bReduceTransparency );
+ m_xReduceTransparencyNoneRB->set_sensitive( bReduceTransparency );
+
+ m_xTransparencyCB->set_sensitive( !bReduceTransparency );
+}
+
+IMPL_LINK_NOARG( SfxCommonPrintOptionsTabPage, ClickReduceGradientsCBHdl, weld::Toggleable&, void )
+{
+ const bool bEnable = m_xReduceGradientsCB->get_active();
+
+ m_xReduceGradientsStripesRB->set_sensitive( bEnable );
+ m_xReduceGradientsColorRB->set_sensitive( bEnable );
+ m_xReduceGradientsStepCountNF->set_sensitive( bEnable );
+
+ ToggleReduceGradientsStripesRBHdl(*m_xReduceGradientsStripesRB);
+}
+
+IMPL_LINK_NOARG( SfxCommonPrintOptionsTabPage, ClickReduceBitmapsCBHdl, weld::Toggleable&, void )
+{
+ const bool bEnable = m_xReduceBitmapsCB->get_active();
+
+ m_xReduceBitmapsOptimalRB->set_sensitive( bEnable );
+ m_xReduceBitmapsNormalRB->set_sensitive( bEnable );
+ m_xReduceBitmapsResolutionRB->set_sensitive( bEnable );
+ m_xReduceBitmapsTransparencyCB->set_sensitive( bEnable );
+ m_xReduceBitmapsResolutionLB->set_sensitive( bEnable );
+
+ ToggleReduceBitmapsResolutionRBHdl(*m_xReduceBitmapsResolutionRB);
+}
+
+IMPL_LINK_NOARG( SfxCommonPrintOptionsTabPage, ToggleReduceGradientsStripesRBHdl, weld::Toggleable&, void )
+{
+ const bool bEnable = m_xReduceGradientsCB->get_active() && m_xReduceGradientsStripesRB->get_active();
+
+ m_xReduceGradientsStepCountNF->set_sensitive(bEnable);
+}
+
+IMPL_LINK_NOARG( SfxCommonPrintOptionsTabPage, ToggleReduceBitmapsResolutionRBHdl, weld::Toggleable&, void )
+{
+ const bool bEnable = m_xReduceBitmapsCB->get_active() && m_xReduceBitmapsResolutionRB->get_active();
+
+ m_xReduceBitmapsResolutionLB->set_sensitive(bEnable);
+}
+
+IMPL_LINK( SfxCommonPrintOptionsTabPage, ToggleOutputPrinterRBHdl, weld::Toggleable&, rButton, void )
+{
+ if (rButton.get_active())
+ {
+ ImplUpdateControls( &maPrinterOptions );
+ bOutputForPrinter = true;
+ }
+ else
+ ImplSaveControls( &maPrinterOptions );
+}
+
+IMPL_LINK( SfxCommonPrintOptionsTabPage, ToggleOutputPrintFileRBHdl, weld::Toggleable&, rButton, void )
+{
+ if (rButton.get_active())
+ {
+ ImplUpdateControls( &maPrintFileOptions );
+ bOutputForPrinter = false;
+ m_xPDFCB->set_sensitive(false);
+ }
+ else
+ {
+ ImplSaveControls( &maPrintFileOptions );
+ m_xPDFCB->set_sensitive(true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/recfloat.cxx b/sfx2/source/dialog/recfloat.cxx
new file mode 100644
index 000000000..1dcbb2f7c
--- /dev/null
+++ b/sfx2/source/dialog/recfloat.cxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/frame/XDispatchRecorder.hpp>
+
+#include <svl/eitem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/windowstate.hxx>
+
+#include <recfloat.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER(SfxRecordingFloatWrapper_Impl, SID_RECORDING_FLOATWINDOW);
+
+SfxRecordingFloatWrapper_Impl::SfxRecordingFloatWrapper_Impl(vcl::Window* pParentWnd,
+ sal_uInt16 nId,
+ SfxBindings* pBind,
+ SfxChildWinInfo const * pInfo)
+ : SfxChildWindow(pParentWnd, nId)
+ , pBindings(pBind)
+{
+ SetController(std::make_shared<SfxRecordingFloat_Impl>(pBindings, this, pParentWnd->GetFrameWeld()));
+ SetWantsFocus(false);
+ SfxRecordingFloat_Impl* pFloatDlg = static_cast<SfxRecordingFloat_Impl*>(GetController().get());
+
+ weld::Dialog* pDlg = pFloatDlg->getDialog();
+
+ SfxViewFrame *pFrame = pBind->GetDispatcher_Impl()->GetFrame();
+ vcl::Window* pEditWin = pFrame->GetViewShell()->GetWindow();
+
+ Point aPos = pEditWin->OutputToScreenPixel( pEditWin->GetPosPixel() );
+ aPos.AdjustX(20);
+ aPos.AdjustY(10);
+
+ WindowStateData aState;
+ aState.SetMask(WindowStateMask::Pos);
+ aState.SetX(aPos.X());
+ aState.SetY(aPos.Y());
+ pDlg->set_window_state(aState.ToStr());
+
+ pFloatDlg->Initialize(pInfo);
+}
+
+SfxRecordingFloatWrapper_Impl::~SfxRecordingFloatWrapper_Impl()
+{
+ SfxBoolItem aItem( FN_PARAM_1, true );
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder = pBindings->GetRecorder();
+ if ( xRecorder.is() )
+ pBindings->GetDispatcher()->ExecuteList(SID_STOP_RECORDING,
+ SfxCallMode::SYNCHRON, { &aItem });
+}
+
+bool SfxRecordingFloatWrapper_Impl::QueryClose()
+{
+ // asking for recorded macro should be replaced if index access is available!
+ bool bRet = true;
+ css::uno::Reference< css::frame::XDispatchRecorder > xRecorder = pBindings->GetRecorder();
+ if ( xRecorder.is() && !xRecorder->getRecordedMacro().isEmpty() )
+ {
+ SfxRecordingFloat_Impl* pFloatDlg = static_cast<SfxRecordingFloat_Impl*>(GetController().get());
+ weld::Dialog* pDlg = pFloatDlg->getDialog();
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pDlg,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SfxResId(STR_MACRO_LOSS)));
+ xQueryBox->set_default_response(RET_NO);
+
+ xQueryBox->set_title(SfxResId(STR_CANCEL_RECORDING));
+ bRet = (xQueryBox->run() == RET_YES);
+ }
+
+ return bRet;
+}
+
+SfxRecordingFloat_Impl::SfxRecordingFloat_Impl(SfxBindings* pBind, SfxChildWindow* pChildWin,
+ weld::Window* pParent)
+ : SfxModelessDialogController(pBind, pChildWin, pParent, "sfx/ui/floatingrecord.ui",
+ "FloatingRecord")
+ , m_xToolbar(m_xBuilder->weld_toolbar("toolbar"))
+ , m_xDispatcher(new ToolbarUnoDispatcher(*m_xToolbar, *m_xBuilder, pBind->GetActiveFrame()))
+ , mnPostUserEventId(nullptr)
+ , m_bFirstActivate(true)
+{
+ // start recording
+ SfxBoolItem aItem( SID_RECORDMACRO, true );
+ GetBindings().GetDispatcher()->ExecuteList(SID_RECORDMACRO,
+ SfxCallMode::SYNCHRON, { &aItem });
+}
+
+IMPL_LINK_NOARG(SfxRecordingFloat_Impl, PresentParentFrame, void*, void)
+{
+ mnPostUserEventId = nullptr;
+ css::uno::Reference<css::awt::XTopWindow> xTopWindow(m_xDispatcher->GetFrame()->getContainerWindow(), css::uno::UNO_QUERY);
+ if (xTopWindow.is())
+ xTopWindow->toFront();
+}
+
+void SfxRecordingFloat_Impl::Activate()
+{
+ SfxModelessDialogController::Activate();
+ if (!m_bFirstActivate)
+ return;
+ // tdf#147782 retain focus in launching frame on the first activate on automatically gaining focus on getting launched
+ m_bFirstActivate = false;
+ mnPostUserEventId = Application::PostUserEvent(LINK(this, SfxRecordingFloat_Impl, PresentParentFrame));
+}
+
+SfxRecordingFloat_Impl::~SfxRecordingFloat_Impl()
+{
+ if (mnPostUserEventId)
+ Application::RemoveUserEvent(mnPostUserEventId);
+ m_xDispatcher->dispose();
+}
+
+void SfxRecordingFloat_Impl::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ SfxModelessDialogController::FillInfo( rInfo );
+ rInfo.bVisible = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/securitypage.cxx b/sfx2/source/dialog/securitypage.cxx
new file mode 100644
index 000000000..a07eb4ace
--- /dev/null
+++ b/sfx2/source/dialog/securitypage.cxx
@@ -0,0 +1,451 @@
+/* -*- 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 <sfx2/htmlmode.hxx>
+
+#include <sfx2/sfxresid.hxx>
+
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/passwd.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/eitem.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/PasswordHelper.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+
+#include <sfx2/strings.hrc>
+
+#include "securitypage.hxx"
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ enum RedliningMode { RL_NONE, RL_WRITER, RL_CALC };
+
+ bool QueryState( TypedWhichId<SfxBoolItem> _nSlot, bool& _rValue )
+ {
+ bool bRet = false;
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+ if (pViewSh)
+ {
+ const SfxBoolItem* pItem;
+ SfxDispatcher* pDisp = pViewSh->GetDispatcher();
+ SfxItemState nState = pDisp->QueryState( _nSlot, pItem );
+ bRet = SfxItemState::DEFAULT <= nState;
+ if (bRet)
+ _rValue = pItem->GetValue();
+ }
+ return bRet;
+ }
+
+
+ bool QueryRecordChangesProtectionState( RedliningMode _eMode, bool& _rValue )
+ {
+ bool bRet = false;
+ if (_eMode != RL_NONE)
+ {
+ TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_PROTECT : SID_CHG_PROTECT;
+ bRet = QueryState( nSlot, _rValue );
+ }
+ return bRet;
+ }
+
+
+ bool QueryRecordChangesState( RedliningMode _eMode, bool& _rValue )
+ {
+ bool bRet = false;
+ if (_eMode != RL_NONE)
+ {
+ TypedWhichId<SfxBoolItem> nSlot = _eMode == RL_WRITER ? FN_REDLINE_ON : FID_CHG_RECORD;
+ bRet = QueryState( nSlot, _rValue );
+ }
+ return bRet;
+ }
+}
+
+
+static bool lcl_GetPassword(
+ weld::Window *pParent,
+ bool bProtect,
+ /*out*/OUString &rPassword )
+{
+ bool bRes = false;
+ SfxPasswordDialog aPasswdDlg(pParent);
+ aPasswdDlg.SetMinLen(1);
+ if (bProtect)
+ aPasswdDlg.ShowExtras( SfxShowExtras::CONFIRM );
+ if (RET_OK == aPasswdDlg.run() && !aPasswdDlg.GetPassword().isEmpty())
+ {
+ rPassword = aPasswdDlg.GetPassword();
+ bRes = true;
+ }
+ return bRes;
+}
+
+
+static bool lcl_IsPasswordCorrect( std::u16string_view rPassword )
+{
+ SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
+ if (!pCurDocShell)
+ return false;
+
+ bool bRes = false;
+ uno::Sequence< sal_Int8 > aPasswordHash;
+ pCurDocShell->GetProtectionHash( aPasswordHash );
+
+ // check if supplied password was correct
+ if (aPasswordHash.getLength() == 1 && aPasswordHash[0] == 1)
+ {
+ // dummy RedlinePassword from OOXML import: get real password info
+ // from the grab-bag to verify the password
+ const css::uno::Sequence< css::beans::PropertyValue > aDocumentProtection =
+ pCurDocShell->GetDocumentProtectionFromGrabBag();
+ bRes =
+ // password is ok, if there is no DocumentProtection in the GrabBag,
+ // i.e. the dummy RedlinePassword imported from an OpenDocument file
+ !aDocumentProtection.hasElements() ||
+ // verify password with the password info imported from OOXML
+ ::comphelper::DocPasswordHelper::IsModifyPasswordCorrect( rPassword,
+ ::comphelper::DocPasswordHelper::ConvertPasswordInfo ( aDocumentProtection ) );
+ }
+ else
+ {
+ uno::Sequence< sal_Int8 > aNewPasswd( aPasswordHash );
+ SvPasswordHelper::GetHashPassword( aNewPasswd, rPassword );
+ bRes = SvPasswordHelper::CompareHashPassword( aPasswordHash, rPassword );
+ }
+
+ if ( !bRes )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ SfxResId(RID_SVXSTR_INCORRECT_PASSWORD)));
+ xInfoBox->run();
+ }
+
+ return bRes;
+}
+
+struct SfxSecurityPage_Impl
+{
+ SfxSecurityPage & m_rMyTabPage;
+
+ RedliningMode m_eRedlingMode; // for record changes
+
+ bool m_bOrigPasswordIsConfirmed;
+ bool m_bNewPasswordIsValid;
+ OUString m_aNewPassword;
+
+ OUString m_aEndRedliningWarning;
+ bool m_bEndRedliningWarningDone;
+
+ std::unique_ptr<weld::CheckButton> m_xOpenReadonlyCB;
+ std::unique_ptr<weld::CheckButton> m_xRecordChangesCB; // for record changes
+ std::unique_ptr<weld::Button> m_xProtectPB; // for record changes
+ std::unique_ptr<weld::Button> m_xUnProtectPB; // for record changes
+
+ DECL_LINK(RecordChangesCBToggleHdl, weld::Toggleable&, void);
+ DECL_LINK(ChangeProtectionPBHdl, weld::Button&, void);
+
+ SfxSecurityPage_Impl( SfxSecurityPage &rDlg );
+
+ bool FillItemSet_Impl();
+ void Reset_Impl();
+};
+
+SfxSecurityPage_Impl::SfxSecurityPage_Impl(SfxSecurityPage &rTabPage)
+ : m_rMyTabPage(rTabPage)
+ , m_eRedlingMode(RL_NONE)
+ , m_bOrigPasswordIsConfirmed(false)
+ , m_bNewPasswordIsValid(false)
+ , m_aEndRedliningWarning(SfxResId(RID_SVXSTR_END_REDLINING_WARNING))
+ , m_bEndRedliningWarningDone(false)
+ , m_xOpenReadonlyCB(rTabPage.GetBuilder().weld_check_button("readonly"))
+ , m_xRecordChangesCB(rTabPage.GetBuilder().weld_check_button("recordchanges"))
+ , m_xProtectPB(rTabPage.GetBuilder().weld_button("protect"))
+ , m_xUnProtectPB(rTabPage.GetBuilder().weld_button("unprotect"))
+{
+ m_xProtectPB->show();
+ m_xUnProtectPB->hide();
+
+ m_xRecordChangesCB->connect_toggled(LINK(this, SfxSecurityPage_Impl, RecordChangesCBToggleHdl));
+ m_xProtectPB->connect_clicked(LINK(this, SfxSecurityPage_Impl, ChangeProtectionPBHdl));
+ m_xUnProtectPB->connect_clicked(LINK(this, SfxSecurityPage_Impl, ChangeProtectionPBHdl));
+}
+
+bool SfxSecurityPage_Impl::FillItemSet_Impl()
+{
+ bool bModified = false;
+
+ SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
+ if (pCurDocShell && !pCurDocShell->IsReadOnly())
+ {
+ if (m_eRedlingMode != RL_NONE )
+ {
+ const bool bDoRecordChanges = m_xRecordChangesCB->get_active();
+ const bool bDoChangeProtection = m_xUnProtectPB->get_visible();
+
+ // sanity checks
+ DBG_ASSERT( bDoRecordChanges || !bDoChangeProtection, "no change recording should imply no change protection" );
+ DBG_ASSERT( bDoChangeProtection || !bDoRecordChanges, "no change protection should imply no change recording" );
+ DBG_ASSERT( !bDoChangeProtection || !m_aNewPassword.isEmpty(), "change protection should imply password length is > 0" );
+ DBG_ASSERT( bDoChangeProtection || m_aNewPassword.isEmpty(), "no change protection should imply password length is 0" );
+
+ // change recording
+ if (bDoRecordChanges != pCurDocShell->IsChangeRecording())
+ {
+ pCurDocShell->SetChangeRecording( bDoRecordChanges );
+ bModified = true;
+ }
+
+ // change record protection
+ if (m_bNewPasswordIsValid &&
+ bDoChangeProtection != pCurDocShell->HasChangeRecordProtection())
+ {
+ DBG_ASSERT( !bDoChangeProtection || bDoRecordChanges,
+ "change protection requires record changes to be active!" );
+ pCurDocShell->SetProtectionPassword( m_aNewPassword );
+ bModified = true;
+ }
+ }
+
+ // open read-only?
+ const bool bDoOpenReadonly = m_xOpenReadonlyCB->get_active();
+ if (bDoOpenReadonly != pCurDocShell->IsSecurityOptOpenReadOnly())
+ {
+ pCurDocShell->SetSecurityOptOpenReadOnly( bDoOpenReadonly );
+ bModified = true;
+ }
+ }
+
+ return bModified;
+}
+
+
+void SfxSecurityPage_Impl::Reset_Impl()
+{
+ SfxObjectShell* pCurDocShell = SfxObjectShell::Current();
+
+ if (!pCurDocShell)
+ {
+ // no doc -> hide document settings
+ m_xOpenReadonlyCB->set_sensitive(false);
+ m_xRecordChangesCB->set_sensitive(false);
+ m_xProtectPB->show();
+ m_xProtectPB->set_sensitive(false);
+ m_xUnProtectPB->hide();
+ m_xUnProtectPB->set_sensitive(false);
+ }
+ else
+ {
+ bool bIsHTMLDoc = false;
+ bool bProtect = true, bUnProtect = false;
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+ if (pViewSh)
+ {
+ const SfxUInt16Item* pItem;
+ SfxDispatcher* pDisp = pViewSh->GetDispatcher();
+ if (SfxItemState::DEFAULT <= pDisp->QueryState( SID_HTML_MODE, pItem ))
+ {
+ sal_uInt16 nMode = pItem->GetValue();
+ bIsHTMLDoc = ( ( nMode & HTMLMODE_ON ) != 0 );
+ }
+ }
+
+ bool bIsReadonly = pCurDocShell->IsReadOnly();
+ if (!bIsHTMLDoc)
+ {
+ m_xOpenReadonlyCB->set_active(pCurDocShell->IsSecurityOptOpenReadOnly());
+ m_xOpenReadonlyCB->set_sensitive(!bIsReadonly);
+ }
+ else
+ m_xOpenReadonlyCB->set_sensitive(false);
+
+ bool bRecordChanges;
+ if (QueryRecordChangesState( RL_WRITER, bRecordChanges ) && !bIsHTMLDoc)
+ m_eRedlingMode = RL_WRITER;
+ else if (QueryRecordChangesState( RL_CALC, bRecordChanges ))
+ m_eRedlingMode = RL_CALC;
+ else
+ m_eRedlingMode = RL_NONE;
+
+ if (m_eRedlingMode != RL_NONE)
+ {
+ bool bProtection(false);
+ QueryRecordChangesProtectionState( m_eRedlingMode, bProtection );
+
+ m_xProtectPB->set_sensitive(!bIsReadonly);
+ m_xUnProtectPB->set_sensitive(!bIsReadonly);
+ // set the right text
+ if (bProtection)
+ {
+ bProtect = false;
+ bUnProtect = true;
+ }
+
+ m_xRecordChangesCB->set_active(bRecordChanges);
+ m_xRecordChangesCB->set_sensitive(/*!bProtection && */!bIsReadonly);
+
+ m_bOrigPasswordIsConfirmed = true; // default case if no password is set
+ uno::Sequence< sal_Int8 > aPasswordHash;
+ // check if password is available
+ if (pCurDocShell->GetProtectionHash( aPasswordHash ) &&
+ aPasswordHash.hasElements())
+ m_bOrigPasswordIsConfirmed = false; // password found, needs to be confirmed later on
+ }
+ else
+ {
+ // A Calc document that is shared will have 'm_eRedlingMode == RL_NONE'
+ // In shared documents change recording and protection must be disabled,
+ // similar to documents that do not support change recording at all.
+ m_xRecordChangesCB->set_active(false);
+ m_xRecordChangesCB->set_sensitive(false);
+ m_xProtectPB->set_sensitive(false);
+ m_xUnProtectPB->set_sensitive(false);
+ }
+
+ m_xProtectPB->set_visible(bProtect);
+ m_xUnProtectPB->set_visible(bUnProtect);
+ }
+}
+
+IMPL_LINK_NOARG(SfxSecurityPage_Impl, RecordChangesCBToggleHdl, weld::Toggleable&, void)
+{
+ // when change recording gets disabled protection must be disabled as well
+ if (m_xRecordChangesCB->get_active()) // the new check state is already present, thus the '!'
+ return;
+
+ bool bAlreadyDone = false;
+ if (!m_bEndRedliningWarningDone)
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_rMyTabPage.GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::YesNo,
+ m_aEndRedliningWarning));
+ xWarn->set_default_response(RET_NO);
+ if (xWarn->run() != RET_YES)
+ bAlreadyDone = true;
+ else
+ m_bEndRedliningWarningDone = true;
+ }
+
+ const bool bNeedPassword = !m_bOrigPasswordIsConfirmed
+ && m_xUnProtectPB->get_visible(); // tdf#128230 Require password if the Unprotect button is visible
+ if (!bAlreadyDone && bNeedPassword)
+ {
+ OUString aPasswordText;
+
+ // dialog canceled or no password provided
+ if (!lcl_GetPassword( m_rMyTabPage.GetFrameWeld(), false, aPasswordText ))
+ bAlreadyDone = true;
+
+ // ask for password and if dialog is canceled or no password provided return
+ if (lcl_IsPasswordCorrect( aPasswordText ))
+ m_bOrigPasswordIsConfirmed = true;
+ else
+ bAlreadyDone = true;
+ }
+
+ if (bAlreadyDone)
+ m_xRecordChangesCB->set_active(true); // restore original state
+ else
+ {
+ // remember required values to change protection and change recording in
+ // FillItemSet_Impl later on if password was correct.
+ m_bNewPasswordIsValid = true;
+ m_aNewPassword.clear();
+ m_xProtectPB->show();
+ m_xUnProtectPB->hide();
+ }
+}
+
+IMPL_LINK_NOARG(SfxSecurityPage_Impl, ChangeProtectionPBHdl, weld::Button&, void)
+{
+ if (m_eRedlingMode == RL_NONE)
+ return;
+
+ // the push button text is always the opposite of the current state. Thus:
+ const bool bCurrentProtection = m_xUnProtectPB->get_visible();
+
+ // ask user for password (if still necessary)
+ OUString aPasswordText;
+ bool bNewProtection = !bCurrentProtection;
+ const bool bNeedPassword = bNewProtection || !m_bOrigPasswordIsConfirmed;
+ if (bNeedPassword)
+ {
+ // ask for password and if dialog is canceled or no password provided return
+ if (!lcl_GetPassword(m_rMyTabPage.GetFrameWeld(), bNewProtection, aPasswordText))
+ return;
+
+ // provided password still needs to be checked?
+ if (!bNewProtection && !m_bOrigPasswordIsConfirmed)
+ {
+ if (lcl_IsPasswordCorrect( aPasswordText ))
+ m_bOrigPasswordIsConfirmed = true;
+ else
+ return;
+ }
+ }
+ DBG_ASSERT( m_bOrigPasswordIsConfirmed, "ooops... this should not have happened!" );
+
+ // remember required values to change protection and change recording in
+ // FillItemSet_Impl later on if password was correct.
+ m_bNewPasswordIsValid = true;
+ m_aNewPassword = bNewProtection? aPasswordText : OUString();
+
+ m_xRecordChangesCB->set_active(bNewProtection);
+
+ m_xUnProtectPB->set_visible(bNewProtection);
+ m_xProtectPB->set_visible(!bNewProtection);
+}
+
+std::unique_ptr<SfxTabPage> SfxSecurityPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet * rItemSet)
+{
+ return std::make_unique<SfxSecurityPage>(pPage, pController, *rItemSet);
+}
+
+SfxSecurityPage::SfxSecurityPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
+ : SfxTabPage(pPage, pController, "sfx/ui/securityinfopage.ui", "SecurityInfoPage", &rItemSet)
+{
+ m_pImpl.reset(new SfxSecurityPage_Impl( *this ));
+}
+
+bool SfxSecurityPage::FillItemSet( SfxItemSet * /*rItemSet*/ )
+{
+ bool bModified = false;
+ DBG_ASSERT(m_pImpl, "implementation pointer is 0. Still in c-tor?");
+ if (m_pImpl != nullptr)
+ bModified = m_pImpl->FillItemSet_Impl();
+ return bModified;
+}
+
+void SfxSecurityPage::Reset( const SfxItemSet * /*rItemSet*/ )
+{
+ DBG_ASSERT(m_pImpl, "implementation pointer is 0. Still in c-tor?");
+ if (m_pImpl != nullptr)
+ m_pImpl->Reset_Impl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/securitypage.hxx b/sfx2/source/dialog/securitypage.hxx
new file mode 100644
index 000000000..a598dfeb4
--- /dev/null
+++ b/sfx2/source/dialog/securitypage.hxx
@@ -0,0 +1,43 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SFX2_SECURITYPAGE_HXX
+#define INCLUDED_SFX2_SECURITYPAGE_HXX
+
+#include <sfx2/tabdlg.hxx>
+#include <memory>
+
+struct SfxSecurityPage_Impl;
+
+class SfxSecurityPage final : public SfxTabPage
+{
+ std::unique_ptr<SfxSecurityPage_Impl> m_pImpl;
+
+ virtual bool FillItemSet(SfxItemSet*) override;
+ virtual void Reset(const SfxItemSet*) override;
+
+public:
+ SfxSecurityPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet&);
+ static std::unique_ptr<SfxTabPage>
+ Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet*);
+ weld::Builder& GetBuilder() const { return *m_xBuilder; }
+};
+
+#endif // INCLUDED_SFX2_SECURITYPAGE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/sfxdlg.cxx b/sfx2/source/dialog/sfxdlg.cxx
new file mode 100644
index 000000000..4098dedd9
--- /dev/null
+++ b/sfx2/source/dialog/sfxdlg.cxx
@@ -0,0 +1,29 @@
+/* -*- 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 <sfx2/sfxdlg.hxx>
+
+SfxAbstractDialogFactory* SfxAbstractDialogFactory::Create()
+{
+ return dynamic_cast<SfxAbstractDialogFactory*>(VclAbstractDialogFactory::Create());
+}
+
+SfxAbstractDialogFactory::~SfxAbstractDialogFactory() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/splitwin.cxx b/sfx2/source/dialog/splitwin.cxx
new file mode 100644
index 000000000..2abedce11
--- /dev/null
+++ b/sfx2/source/dialog/splitwin.cxx
@@ -0,0 +1,1155 @@
+/* -*- 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 .
+ */
+
+#ifdef __sun
+#include <ctime>
+#endif
+
+#include <unotools/viewoptions.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+#include <vcl/dialoghelper.hxx>
+#include <vcl/event.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/svapp.hxx>
+
+#include <splitwin.hxx>
+#include <workwin.hxx>
+#include <sfx2/dockwin.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <memory>
+#include <vector>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+
+#define VERSION 1
+#define nPixel 30L
+constexpr OUStringLiteral USERITEM_NAME = u"UserItem";
+
+namespace {
+ // helper class to deactivate UpdateMode, if needed, for the life time of an instance
+ class DeactivateUpdateMode
+ {
+ public:
+ explicit DeactivateUpdateMode( SfxSplitWindow& rSplitWindow )
+ : mrSplitWindow( rSplitWindow )
+ , mbUpdateMode( rSplitWindow.IsUpdateMode() )
+ {
+ if ( mbUpdateMode )
+ {
+ mrSplitWindow.SetUpdateMode( false );
+ }
+ }
+
+ ~DeactivateUpdateMode()
+ {
+ if ( mbUpdateMode )
+ {
+ mrSplitWindow.SetUpdateMode( true );
+ }
+ }
+
+ private:
+ SfxSplitWindow& mrSplitWindow;
+ const bool mbUpdateMode;
+ };
+}
+
+class SfxEmptySplitWin_Impl : public SplitWindow
+{
+/* [Description]
+
+ The SfxEmptySplitWin_Impldow is an empty SplitWindow, that replaces the
+ SfxSplitWindow AutoHide mode. It only serves as a placeholder to receive
+ mouse moves and if possible blend in the true SplitWindow display.
+*/
+friend class SfxSplitWindow;
+
+ VclPtr<SfxSplitWindow> pOwner;
+ bool bFadeIn;
+ bool bAutoHide;
+ bool bSplit;
+ bool bEndAutoHide;
+ Timer aTimer;
+ Point aLastPos;
+ sal_uInt16 nState;
+
+public:
+ explicit SfxEmptySplitWin_Impl( SfxSplitWindow *pParent )
+ : SplitWindow( pParent->GetParent(), WinBits( WB_BORDER | WB_3DLOOK ) )
+ , pOwner( pParent )
+ , bFadeIn( false )
+ , bAutoHide( false )
+ , bSplit( false )
+ , bEndAutoHide( false )
+ , aTimer("sfx2 SfxEmptySplitWin_Impl aTimer")
+ , nState( 1 )
+ {
+ aTimer.SetInvokeHandler(
+ LINK(pOwner, SfxSplitWindow, TimerHdl ) );
+ aTimer.SetTimeout( 200 );
+ SetAlign( pOwner->GetAlign() );
+ Actualize();
+ ShowFadeInHideButton();
+ }
+
+ virtual ~SfxEmptySplitWin_Impl() override
+ { disposeOnce(); }
+ virtual void dispose() override
+ {
+ aTimer.Stop();
+ pOwner.clear();
+ SplitWindow::dispose();
+ }
+
+ virtual void FadeIn() override;
+ void Actualize();
+};
+
+void SfxEmptySplitWin_Impl::Actualize()
+{
+ Size aSize( pOwner->GetSizePixel() );
+ switch ( pOwner->GetAlign() )
+ {
+ case WindowAlign::Left:
+ case WindowAlign::Right:
+ aSize.setWidth( GetFadeInSize() );
+ break;
+ case WindowAlign::Top:
+ case WindowAlign::Bottom:
+ aSize.setHeight( GetFadeInSize() );
+ break;
+ }
+
+ SetSizePixel( aSize );
+}
+
+void SfxEmptySplitWin_Impl::FadeIn()
+{
+ if (!bAutoHide )
+ bAutoHide = IsFadeNoButtonMode();
+ pOwner->SetFadeIn_Impl( true );
+ if ( bAutoHide )
+ {
+ // Set Timer to close; the caller has to ensure themselves that the
+ // Window is not closed instantly (eg by setting the focus or a modal
+ // mode.
+ aLastPos = GetPointerPosPixel();
+ aTimer.Start();
+ }
+ else
+ pOwner->SaveConfig_Impl();
+}
+
+
+void SfxSplitWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( rMEvt.GetClicks() != 2 )
+ SplitWindow::MouseButtonDown( rMEvt );
+}
+
+SfxSplitWindow::SfxSplitWindow( vcl::Window* pParent, SfxChildAlignment eAl,
+ SfxWorkWindow *pW, bool bWithButtons )
+
+/* [Description]
+
+ A SfxSplitWindow brings the recursive structure of the SV-SplitWindows to
+ the outside by simulating a table-like structure with rows and columns
+ (maximum recursion depth 2). Furthermore, it ensures the persistence of
+ the arrangement of the SfxDockingWindows.
+*/
+
+: SplitWindow ( pParent, WB_BORDER | WB_SIZEABLE | WB_3DLOOK | WB_HIDE ),
+ eAlign(eAl),
+ pWorkWin(pW),
+ bPinned(true),
+ pEmptyWin(nullptr),
+ pActive(nullptr)
+{
+ if (bWithButtons)
+ {
+ ShowFadeOutButton();
+ }
+
+ // Set SV-Alignment
+ WindowAlign eTbxAlign;
+ switch ( eAlign )
+ {
+ case SfxChildAlignment::LEFT:
+ eTbxAlign = WindowAlign::Left;
+ break;
+ case SfxChildAlignment::RIGHT:
+ eTbxAlign = WindowAlign::Right;
+ break;
+ case SfxChildAlignment::TOP:
+ eTbxAlign = WindowAlign::Top;
+ break;
+ case SfxChildAlignment::BOTTOM:
+ eTbxAlign = WindowAlign::Bottom;
+ bPinned = true;
+ break;
+ default:
+ eTbxAlign = WindowAlign::Top; // some sort of default...
+ break; // -Wall lots not handled...
+ }
+
+ SetAlign (eTbxAlign);
+ pEmptyWin = VclPtr<SfxEmptySplitWin_Impl>::Create( this );
+ if ( bPinned )
+ {
+ pEmptyWin->bFadeIn = true;
+ pEmptyWin->nState = 2;
+ }
+
+ if ( bWithButtons )
+ {
+ // Read Configuration
+ const OUString aWindowId{ "SplitWindow" + OUString::number(static_cast<sal_Int32>(eTbxAlign)) };
+ SvtViewOptions aWinOpt( EViewType::Window, aWindowId );
+ OUString aWinData;
+ Any aUserItem = aWinOpt.GetUserItem( USERITEM_NAME );
+ OUString aTemp;
+ if ( aUserItem >>= aTemp )
+ aWinData = aTemp;
+ if ( aWinData.startsWith("V") )
+ {
+ sal_Int32 nIdx{ 0 };
+ pEmptyWin->nState = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 1, ',', nIdx )));
+ if ( pEmptyWin->nState & 2 )
+ pEmptyWin->bFadeIn = true;
+ bPinned = true; // always assume pinned - floating mode not used anymore
+
+ const sal_Int32 nCount{ o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)) };
+ for ( sal_Int32 n=0; n<nCount; ++n )
+ {
+ std::unique_ptr<SfxDock_Impl> pDock(new SfxDock_Impl);
+ pDock->pWin = nullptr;
+ pDock->bNewLine = false;
+ pDock->bHide = true;
+ pDock->nType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)));
+ if ( !pDock->nType )
+ {
+ // could mean NewLine
+ pDock->nType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aWinData, 0, ',', nIdx)));
+ if ( !pDock->nType )
+ {
+ // Read error
+ break;
+ }
+ else
+ pDock->bNewLine = true;
+ }
+
+ maDockArr.insert(maDockArr.begin() + n, std::move(pDock));
+ }
+ }
+ }
+ else
+ {
+ bPinned = true;
+ pEmptyWin->bFadeIn = true;
+ pEmptyWin->nState = 2;
+ }
+}
+
+
+SfxSplitWindow::~SfxSplitWindow()
+{
+ disposeOnce();
+}
+
+void SfxSplitWindow::dispose()
+{
+ SaveConfig_Impl();
+
+ if ( pEmptyWin )
+ {
+ // Set pOwner to NULL, otherwise try to delete pEmptyWin once more. The
+ // window that is just being docked is always deleted from the outside.
+ pEmptyWin->pOwner = nullptr;
+ }
+ pEmptyWin.disposeAndClear();
+
+ maDockArr.clear();
+ pActive.clear();
+ SplitWindow::dispose();
+}
+
+void SfxSplitWindow::SaveConfig_Impl()
+{
+ // Save configuration
+ OUStringBuffer aWinData;
+ aWinData.append('V');
+ aWinData.append(static_cast<sal_Int32>(VERSION));
+ aWinData.append(',');
+ aWinData.append(static_cast<sal_Int32>(pEmptyWin->nState));
+ aWinData.append(',');
+
+ sal_uInt16 nCount = 0;
+ for ( auto const & rDock: maDockArr )
+ {
+ if ( rDock->bHide || rDock->pWin )
+ nCount++;
+ }
+
+ aWinData.append(static_cast<sal_Int32>(nCount));
+
+ for ( auto const & rDock: maDockArr )
+ {
+ if ( !rDock->bHide && !rDock->pWin )
+ continue;
+ if ( rDock->bNewLine )
+ aWinData.append(",0");
+ aWinData.append(',');
+ aWinData.append(static_cast<sal_Int32>(rDock->nType));
+ }
+
+ const OUString aWindowId{ "SplitWindow" + OUString::number(static_cast<sal_Int32>(GetAlign())) };
+ SvtViewOptions aWinOpt( EViewType::Window, aWindowId );
+ aWinOpt.SetUserItem( USERITEM_NAME, Any( aWinData.makeStringAndClear() ) );
+}
+
+
+void SfxSplitWindow::StartSplit()
+{
+ tools::Long nSize = 0;
+ Size aSize = GetSizePixel();
+
+ if ( pEmptyWin )
+ {
+ pEmptyWin->bFadeIn = true;
+ pEmptyWin->bSplit = true;
+ }
+
+ tools::Rectangle aRect = pWorkWin->GetFreeArea( !bPinned );
+ switch ( GetAlign() )
+ {
+ case WindowAlign::Left:
+ case WindowAlign::Right:
+ nSize = aSize.Width() + aRect.GetWidth();
+ break;
+ case WindowAlign::Top:
+ case WindowAlign::Bottom:
+ nSize = aSize.Height() + aRect.GetHeight();
+ break;
+ }
+
+ SetMaxSizePixel( nSize );
+}
+
+
+void SfxSplitWindow::SplitResize()
+{
+ if ( bPinned )
+ {
+ pWorkWin->ArrangeChildren_Impl();
+ pWorkWin->ShowChildren_Impl();
+ }
+ else
+ pWorkWin->ArrangeAutoHideWindows( this );
+}
+
+
+void SfxSplitWindow::Split()
+{
+ if ( pEmptyWin )
+ pEmptyWin->bSplit = false;
+
+ SplitWindow::Split();
+
+ std::vector< std::pair< sal_uInt16, tools::Long > > aNewOrgSizes;
+
+ sal_uInt16 nCount = maDockArr.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ const SfxDock_Impl& rD = *maDockArr[n];
+ if ( rD.pWin )
+ {
+ const sal_uInt16 nId = rD.nType;
+ const tools::Long nSize = GetItemSize( nId, SplitWindowItemFlags::Fixed );
+ const tools::Long nSetSize = GetItemSize( GetSet( nId ) );
+ Size aSize;
+
+ if ( IsHorizontal() )
+ {
+ aSize.setWidth( nSize );
+ aSize.setHeight( nSetSize );
+ }
+ else
+ {
+ aSize.setWidth( nSetSize );
+ aSize.setHeight( nSize );
+ }
+
+ rD.pWin->SetItemSize_Impl( aSize );
+
+ aNewOrgSizes.emplace_back( nId, nSize );
+ }
+ }
+
+ // workaround insufficiency of <SplitWindow> regarding dock layouting:
+ // apply FIXED item size as 'original' item size to improve layouting of undock-dock-cycle of a window
+ {
+ DeactivateUpdateMode aDeactivateUpdateMode( *this );
+ for (const std::pair< sal_uInt16, tools::Long > & rNewOrgSize : aNewOrgSizes)
+ {
+ SetItemSize( rNewOrgSize.first, rNewOrgSize.second );
+ }
+ }
+
+ SaveConfig_Impl();
+}
+
+
+void SfxSplitWindow::InsertWindow( SfxDockingWindow* pDockWin, const Size& rSize)
+
+/*
+ To insert SfxDockingWindows just pass no position. The SfxSplitWindow
+ searches the last marked one to the passed SfxDockingWindow or appends a
+ new one at the end.
+*/
+{
+ short nLine = -1; // so that the first window cab set nline to 0
+ sal_uInt16 nL;
+ sal_uInt16 nPos = 0;
+ bool bNewLine = true;
+ bool bSaveConfig = false;
+ SfxDock_Impl *pFoundDock=nullptr;
+ sal_uInt16 nCount = maDockArr.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ SfxDock_Impl& rDock = *maDockArr[n];
+ if ( rDock.bNewLine )
+ {
+ // The window opens a new line
+ if ( pFoundDock )
+ // But after the just inserted window
+ break;
+
+ // New line
+ nPos = 0;
+ bNewLine = true;
+ }
+
+ if ( rDock.pWin )
+ {
+ // Does there exist a window now at this position
+ if ( bNewLine && !pFoundDock )
+ {
+ // Not known until now in which real line it is located
+ GetWindowPos( rDock.pWin, nL, nPos );
+ nLine = static_cast<short>(nL);
+ }
+
+ if ( !pFoundDock )
+ {
+ // The window is located before the inserted one
+ nPos++;
+ }
+
+ // Line is opened
+ bNewLine = false;
+ if ( pFoundDock )
+ break;
+ }
+
+ if ( rDock.nType == pDockWin->GetType() )
+ {
+ DBG_ASSERT( !pFoundDock && !rDock.pWin, "Window already exists!");
+ pFoundDock = &rDock;
+ if ( !bNewLine )
+ break;
+ else
+ {
+ // A new line has been created but no window was found there;
+ // continue searching for a window in this line in-order to set
+ // bNewLine correctly. While doing so nline or nPos are not
+ // to be changed!
+ nLine++;
+ }
+ }
+ }
+
+ if ( !pFoundDock )
+ {
+ // Not found, insert at end
+ pFoundDock = new SfxDock_Impl;
+ pFoundDock->bHide = true;
+ maDockArr.push_back( std::unique_ptr<SfxDock_Impl>(pFoundDock) );
+ pFoundDock->nType = pDockWin->GetType();
+ nLine++;
+ nPos = 0;
+ bNewLine = true;
+ pFoundDock->bNewLine = bNewLine;
+ bSaveConfig = true;
+ }
+
+ pFoundDock->pWin = pDockWin;
+ pFoundDock->bHide = false;
+ InsertWindow_Impl( pFoundDock, rSize, nLine, nPos, bNewLine );
+ if ( bSaveConfig )
+ SaveConfig_Impl();
+}
+
+
+void SfxSplitWindow::ReleaseWindow_Impl(SfxDockingWindow const *pDockWin, bool bSave)
+{
+// The docking window is no longer stored in the internal data.
+ sal_uInt16 nCount = maDockArr.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ const SfxDock_Impl& rDock = *maDockArr[n];
+ if ( rDock.nType == pDockWin->GetType() )
+ {
+ if ( rDock.bNewLine && n<nCount-1 )
+ maDockArr[n+1]->bNewLine = true;
+
+ // Window has a position, this we forget
+ maDockArr.erase(maDockArr.begin() + n);
+ break;
+ }
+ }
+
+ if ( bSave )
+ SaveConfig_Impl();
+}
+
+
+void SfxSplitWindow::MoveWindow( SfxDockingWindow* pDockWin, const Size& rSize,
+ sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
+
+/* [Description]
+
+ The docking window is moved within the SplitWindows.
+*/
+
+{
+ sal_uInt16 nL, nP;
+ GetWindowPos( pDockWin, nL, nP );
+
+ if ( nLine > nL && GetItemCount( GetItemId( nL ) ) == 1 )
+ {
+ // If the last window is removed from its line, then everything slips
+ // one line to the front!
+ nLine--;
+ }
+ RemoveWindow( pDockWin );
+ InsertWindow( pDockWin, rSize, nLine, nPos, bNewLine );
+}
+
+
+void SfxSplitWindow::InsertWindow( SfxDockingWindow* pDockWin, const Size& rSize,
+ sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
+
+/* [Description]
+
+ The DockingWindow that is pushed on this SplitWindow and shall hold the
+ given position and size.
+*/
+{
+ ReleaseWindow_Impl( pDockWin, false );
+ SfxDock_Impl *pDock = new SfxDock_Impl;
+ pDock->bHide = false;
+ pDock->nType = pDockWin->GetType();
+ pDock->bNewLine = bNewLine;
+ pDock->pWin = pDockWin;
+
+ DBG_ASSERT( nPos==0 || !bNewLine, "Wrong Parameter!");
+ if ( bNewLine )
+ nPos = 0;
+
+ // The window must be inserted before the first window so that it has the
+ // same or a greater position than pDockWin.
+ sal_uInt16 nCount = maDockArr.size();
+ sal_uInt16 nLastWindowIdx(0);
+
+ // If no window is found, a first window is inserted
+ sal_uInt16 nInsertPos = 0;
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ SfxDock_Impl& rD = *maDockArr[n];
+
+ if (rD.pWin)
+ {
+ // A docked window has been found. If no suitable window behind
+ // the desired insertion point s found, then insertion is done at
+ // the end.
+ nInsertPos = nCount;
+ nLastWindowIdx = n;
+ sal_uInt16 nL=0, nP=0;
+ GetWindowPos( rD.pWin, nL, nP );
+
+ if ( (nL == nLine && nP == nPos) || nL > nLine )
+ {
+ DBG_ASSERT( nL == nLine || bNewLine || nPos > 0, "Wrong Parameter!" );
+ if ( nL == nLine && nPos == 0 && !bNewLine )
+ {
+ DBG_ASSERT(rD.bNewLine, "No new line?");
+
+ // The position is pushed to nPos==0
+ rD.bNewLine = false;
+ pDock->bNewLine = true;
+ }
+
+ nInsertPos = n != 0 ? nLastWindowIdx + 1 : 0; // ignore all non-windows after the last window
+ break;
+ }
+ }
+ }
+ if (nCount != 0 && nInsertPos == nCount && nLastWindowIdx != nCount - 1)
+ {
+ nInsertPos = nLastWindowIdx + 1; // ignore all non-windows after the last window
+ }
+
+ maDockArr.insert(maDockArr.begin() + nInsertPos, std::unique_ptr<SfxDock_Impl>(pDock));
+ InsertWindow_Impl( pDock, rSize, nLine, nPos, bNewLine );
+ SaveConfig_Impl();
+}
+
+
+void SfxSplitWindow::InsertWindow_Impl( SfxDock_Impl const * pDock,
+ const Size& rSize,
+ sal_uInt16 nLine, sal_uInt16 nPos, bool bNewLine)
+
+/* [Description]
+
+ Adds a DockingWindow, and causes the recalculation of the size of
+ the SplitWindows.
+*/
+
+{
+ SfxDockingWindow* pDockWin = pDock->pWin;
+
+ SplitWindowItemFlags nItemBits = SplitWindowItemFlags::NONE;
+
+ tools::Long nWinSize, nSetSize;
+ if ( IsHorizontal() )
+ {
+ nWinSize = rSize.Width();
+ nSetSize = rSize.Height();
+ }
+ else
+ {
+ nSetSize = rSize.Width();
+ nWinSize = rSize.Height();
+ }
+
+ std::unique_ptr<DeactivateUpdateMode> pDeactivateUpdateMode(new DeactivateUpdateMode( *this ));
+
+ if ( bNewLine || nLine == GetItemCount() )
+ {
+ // An existing row should not be inserted, instead a new one
+ // will be created
+
+ sal_uInt16 nId = 1;
+ for ( sal_uInt16 n=0; n<GetItemCount(); n++ )
+ {
+ if ( GetItemId(n) >= nId )
+ nId = GetItemId(n)+1;
+ }
+
+ // Create a new nLine:th line
+ SplitWindowItemFlags nBits = nItemBits;
+ if ( GetAlign() == WindowAlign::Top || GetAlign() == WindowAlign::Bottom )
+ nBits |= SplitWindowItemFlags::ColSet;
+ InsertItem( nId, nSetSize, nLine, 0, nBits );
+ }
+
+ // Insert the window at line with the position nline. ItemWindowSize set to
+ // "percentage" share since the SV then does the re-sizing as expected,
+ // "pixel" actually only makes sense if also items with percentage or
+ // relative sizes are present.
+ nItemBits |= SplitWindowItemFlags::PercentSize;
+ sal_uInt16 nSet = GetItemId( nLine );
+ InsertItem( pDockWin->GetType(), pDockWin, nWinSize, nPos, nSet, nItemBits );
+
+ // SplitWindows are once created in SFX and when inserting the first
+ // DockingWindows is made visible.
+ if ( GetItemCount() == 1 && GetItemCount( 1 ) == 1 )
+ {
+ // The Rearranging in WorkWindow and a Show() on the SplitWindow is
+ // caused by SfxDockingwindow (->SfxWorkWindow::ConfigChild_Impl)
+ if ( !bPinned && !IsFloatingMode() )
+ {
+ bPinned = true;
+ bool bFadeIn = ( pEmptyWin->nState & 2 ) != 0;
+ pEmptyWin->bFadeIn = false;
+ SetPinned_Impl( false );
+ pEmptyWin->Actualize();
+ SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering empty Splitwindow" );
+ pWorkWin->RegisterChild_Impl( *GetSplitWindow(), eAlign )->nVisible = SfxChildVisibility::VISIBLE;
+ // tdf#113539 FadeIn will call ArrangeChildren_Impl() for us, and avoiding extra calls to that
+ // can make a different to load times because it avoids extra accessibility calcs
+ if ( bFadeIn )
+ FadeIn();
+ else
+ pWorkWin->ArrangeChildren_Impl();
+ }
+ else
+ {
+ bool bFadeIn = ( pEmptyWin->nState & 2 ) != 0;
+ pEmptyWin->bFadeIn = false;
+ pEmptyWin->Actualize();
+ if ( !bPinned || !pEmptyWin->bFadeIn )
+ {
+ SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering empty Splitwindow" );
+ }
+ else
+ {
+ SAL_INFO("sfx", "SfxSplitWindow::InsertWindow_Impl - registering real Splitwindow" );
+ }
+ pWorkWin->RegisterChild_Impl( *GetSplitWindow(), eAlign )->nVisible = SfxChildVisibility::VISIBLE;
+ // tdf#113539 FadeIn will call ArrangeChildren_Impl() for us, and avoiding extra calls to that
+ // can make a different to load times because it avoids extra accessibility calcs
+ if ( bFadeIn )
+ FadeIn();
+ else
+ pWorkWin->ArrangeChildren_Impl();
+ }
+
+ pWorkWin->ShowChildren_Impl();
+ }
+
+ pDeactivateUpdateMode.reset();
+
+ // workaround insufficiency of <SplitWindow> regarding dock layouting:
+ // apply FIXED item size as 'original' item size to improve layouting of undock-dock-cycle of a window
+ {
+ std::vector< std::pair< sal_uInt16, tools::Long > > aNewOrgSizes;
+ // get FIXED item sizes
+ sal_uInt16 nCount = maDockArr.size();
+ for ( sal_uInt16 n=0; n<nCount; ++n )
+ {
+ const SfxDock_Impl& rD = *maDockArr[n];
+ if ( rD.pWin )
+ {
+ const sal_uInt16 nId = rD.nType;
+ const tools::Long nSize = GetItemSize( nId, SplitWindowItemFlags::Fixed );
+ aNewOrgSizes.emplace_back( nId, nSize );
+ }
+ }
+ // apply new item sizes
+ DeactivateUpdateMode aDeactivateUpdateMode( *this );
+ for (const std::pair< sal_uInt16, tools::Long > & rNewOrgSize : aNewOrgSizes)
+ {
+ SetItemSize( rNewOrgSize.first, rNewOrgSize.second );
+ }
+ }
+}
+
+
+void SfxSplitWindow::RemoveWindow( SfxDockingWindow const * pDockWin, bool bHide )
+
+/* [Description]
+
+ Removes a DockingWindow. If it was the last one, then the SplitWindow is
+ being hidden.
+*/
+{
+ sal_uInt16 nSet = GetSet( pDockWin->GetType() );
+
+ // SplitWindows are once created in SFX and is made invisible after
+ // removing the last DockingWindows.
+ if ( GetItemCount( nSet ) == 1 && GetItemCount() == 1 )
+ {
+ // The Rearranging in WorkWindow is caused by SfxDockingwindow
+ Hide();
+ pEmptyWin->aTimer.Stop();
+ sal_uInt16 nRealState = pEmptyWin->nState;
+ FadeOut_Impl();
+ pEmptyWin->Hide();
+#ifdef DBG_UTIL
+ if ( !bPinned || !pEmptyWin->bFadeIn )
+ {
+ SAL_INFO("sfx", "SfxSplitWindow::RemoveWindow - releasing empty Splitwindow" );
+ }
+ else
+ {
+ SAL_INFO("sfx", "SfxSplitWindow::RemoveWindow - releasing real Splitwindow" );
+ }
+#endif
+ pWorkWin->ReleaseChild_Impl( *GetSplitWindow() );
+ pEmptyWin->nState = nRealState;
+ pWorkWin->ArrangeAutoHideWindows( this );
+ }
+
+ sal_uInt16 nCount = maDockArr.size();
+ for ( sal_uInt16 n=0; n<nCount; n++ )
+ {
+ SfxDock_Impl& rDock = *maDockArr[n];
+ if ( rDock.nType == pDockWin->GetType() )
+ {
+ rDock.pWin = nullptr;
+ rDock.bHide = bHide;
+ break;
+ }
+ }
+
+ // Remove Windows, and if it was the last of the line, then also remove
+ // the line (line = itemset)
+ DeactivateUpdateMode aDeactivateUpdateMode( *this );
+
+ RemoveItem( pDockWin->GetType() );
+
+ if ( nSet && !GetItemCount( nSet ) )
+ RemoveItem( nSet );
+};
+
+
+bool SfxSplitWindow::GetWindowPos( const SfxDockingWindow* pWindow,
+ sal_uInt16& rLine, sal_uInt16& rPos ) const
+/* [Description]
+
+ Returns the ID of the item sets and items for the DockingWindow in
+ the position passed on the old row / column-name.
+*/
+
+{
+ sal_uInt16 nSet = GetSet ( pWindow->GetType() );
+ if ( nSet == SPLITWINDOW_ITEM_NOTFOUND )
+ return false;
+
+ rPos = GetItemPos( pWindow->GetType(), nSet );
+ rLine = GetItemPos( nSet );
+ return true;
+}
+
+
+bool SfxSplitWindow::GetWindowPos( const Point& rTestPos,
+ sal_uInt16& rLine, sal_uInt16& rPos ) const
+/* [Description]
+
+ Returns the ID of the item sets and items for the DockingWindow in
+ the position passed on the old row / column-name.
+*/
+
+{
+ sal_uInt16 nId = GetItemId( rTestPos );
+ if ( nId == 0 )
+ return false;
+
+ sal_uInt16 nSet = GetSet ( nId );
+ rPos = GetItemPos( nId, nSet );
+ rLine = GetItemPos( nSet );
+ return true;
+}
+
+
+sal_uInt16 SfxSplitWindow::GetLineCount() const
+
+/* [Description]
+
+ Returns the number of rows = number of sub-itemsets in the root set.
+*/
+{
+ return GetItemCount();
+}
+
+
+tools::Long SfxSplitWindow::GetLineSize( sal_uInt16 nLine ) const
+
+/* [Description]
+
+ Returns the Row Height of nline itemset.
+*/
+{
+ sal_uInt16 nId = GetItemId( nLine );
+ return GetItemSize( nId );
+}
+
+
+sal_uInt16 SfxSplitWindow::GetWindowCount( sal_uInt16 nLine ) const
+
+/* [Description]
+
+ Returns the total number of windows
+*/
+{
+ sal_uInt16 nId = GetItemId( nLine );
+ return GetItemCount( nId );
+}
+
+
+sal_uInt16 SfxSplitWindow::GetWindowCount() const
+
+/* [Description]
+
+ Returns the total number of windows
+*/
+{
+ return GetItemCount();
+}
+
+
+IMPL_LINK( SfxSplitWindow, TimerHdl, Timer*, pTimer, void)
+{
+ if ( pTimer )
+ pTimer->Stop();
+
+ if ( CursorIsOverRect() || !pTimer )
+ {
+ // If the cursor is within the window, display the SplitWindow and set
+ // up the timer for close
+ pEmptyWin->bAutoHide = true;
+ if ( !IsVisible() )
+ pEmptyWin->FadeIn();
+
+ pEmptyWin->aLastPos = GetPointerPosPixel();
+ pEmptyWin->aTimer.Start();
+ }
+ else if ( pEmptyWin->bAutoHide )
+ {
+ if ( GetPointerPosPixel() != pEmptyWin->aLastPos )
+ {
+ // The mouse has moved within the running time of the timer, thus
+ // do nothing
+ pEmptyWin->aLastPos = GetPointerPosPixel();
+ pEmptyWin->aTimer.Start();
+ return;
+ }
+
+ // Especially for TF_AUTOSHOW_ON_MOUSEMOVE :
+ // If the window is not visible, there is nothing to do
+ // (user has simply moved the mouse over pEmptyWin)
+ if ( IsVisible() )
+ {
+ pEmptyWin->bEndAutoHide = false;
+ if ( !Application::IsInModalMode() &&
+ !vcl::IsInPopupMenuExecute() &&
+ !pEmptyWin->bSplit && !HasChildPathFocus( true ) )
+ {
+ // While a modal dialog or a popup menu is open or while the
+ // Splitting is done, in any case, do not close. Even as long
+ // as one of the Children has the focus, the window remains
+ // open.
+ pEmptyWin->bEndAutoHide = true;
+ }
+
+ if ( pEmptyWin->bEndAutoHide )
+ {
+ // As far as I am concerned this can be the end of AutoShow
+ // But maybe some other SfxSplitWindow will remain open,
+ // then all others remain open too.
+ if ( !pWorkWin->IsAutoHideMode( this ) )
+ {
+ FadeOut_Impl();
+ pWorkWin->ArrangeAutoHideWindows( this );
+ }
+ else
+ {
+ pEmptyWin->aLastPos = GetPointerPosPixel();
+ pEmptyWin->aTimer.Start();
+ }
+ }
+ else
+ {
+ pEmptyWin->aLastPos = GetPointerPosPixel();
+ pEmptyWin->aTimer.Start();
+ }
+ }
+ }
+}
+
+
+bool SfxSplitWindow::CursorIsOverRect() const
+{
+ bool bVisible = IsVisible();
+
+ // Also, take the collapsed SplitWindow into account
+ Point aPos = pEmptyWin->GetParent()->OutputToScreenPixel( pEmptyWin->GetPosPixel() );
+ Size aSize = pEmptyWin->GetSizePixel();
+
+ tools::Rectangle aRect( aPos, aSize );
+
+ if ( bVisible )
+ {
+ Point aVisPos = GetPosPixel();
+ Size aVisSize = GetSizePixel();
+
+ // Extend with +/- a few pixels, otherwise it is too nervous
+ aVisPos.AdjustX( -(nPixel) );
+ aVisPos.AdjustY( -(nPixel) );
+ aVisSize.AdjustWidth(2 * nPixel );
+ aVisSize.AdjustHeight(2 * nPixel );
+
+ tools::Rectangle aVisRect( aVisPos, aVisSize );
+ aRect = aRect.GetUnion( aVisRect );
+ }
+
+ return aRect.Contains( OutputToScreenPixel( static_cast<vcl::Window*>(const_cast<SfxSplitWindow *>(this))->GetPointerPosPixel() ) );
+}
+
+
+SplitWindow* SfxSplitWindow::GetSplitWindow()
+{
+ if ( !bPinned || !pEmptyWin->bFadeIn )
+ return pEmptyWin;
+ return this;
+}
+
+
+bool SfxSplitWindow::IsFadeIn() const
+{
+ return pEmptyWin->bFadeIn;
+}
+
+bool SfxSplitWindow::IsAutoHide( bool bSelf ) const
+{
+ return bSelf ? pEmptyWin->bAutoHide && !pEmptyWin->bEndAutoHide : pEmptyWin->bAutoHide;
+}
+
+
+void SfxSplitWindow::SetPinned_Impl( bool bOn )
+{
+ if ( bPinned == bOn )
+ return;
+
+ bPinned = bOn;
+ if ( GetItemCount() == 0 )
+ return;
+
+ if ( !bOn )
+ {
+ pEmptyWin->nState |= 1;
+ if ( pEmptyWin->bFadeIn )
+ {
+ // Unregister replacement windows
+ SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - releasing real Splitwindow" );
+ pWorkWin->ReleaseChild_Impl( *this );
+ Hide();
+ pEmptyWin->Actualize();
+ SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - registering empty Splitwindow" );
+ pWorkWin->RegisterChild_Impl( *pEmptyWin, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
+ }
+
+ Point aPos( GetPosPixel() );
+ aPos = GetParent()->OutputToScreenPixel( aPos );
+ SetFloatingPos( aPos );
+ SetFloatingMode( true );
+ GetFloatingWindow()->SetOutputSizePixel( GetOutputSizePixel() );
+
+ if ( pEmptyWin->bFadeIn )
+ Show();
+ }
+ else
+ {
+ pEmptyWin->nState &= ~1;
+ SetOutputSizePixel( GetFloatingWindow()->GetOutputSizePixel() );
+ SetFloatingMode(false);
+
+ if ( pEmptyWin->bFadeIn )
+ {
+ // Unregister replacement windows
+ SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - releasing empty Splitwindow" );
+ pWorkWin->ReleaseChild_Impl( *pEmptyWin );
+ pEmptyWin->Hide();
+ SAL_INFO("sfx", "SfxSplitWindow::SetPinned_Impl - registering real Splitwindow" );
+ pWorkWin->RegisterChild_Impl( *this, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
+ }
+ }
+}
+
+void SfxSplitWindow::SetFadeIn_Impl( bool bOn )
+{
+ if ( bOn == pEmptyWin->bFadeIn )
+ return;
+
+ if ( GetItemCount() == 0 )
+ return;
+
+ pEmptyWin->bFadeIn = bOn;
+ if ( bOn )
+ {
+ pEmptyWin->nState |= 2;
+ if ( IsFloatingMode() )
+ {
+ // FloatingWindow is not visible, thus display it
+ pWorkWin->ArrangeAutoHideWindows( this );
+ Show();
+ }
+ else
+ {
+ SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - releasing empty Splitwindow" );
+ pWorkWin->ReleaseChild_Impl( *pEmptyWin );
+ pEmptyWin->Hide();
+ SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - registering real Splitwindow" );
+ pWorkWin->RegisterChild_Impl( *this, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
+ pWorkWin->ArrangeChildren_Impl();
+ pWorkWin->ShowChildren_Impl();
+ }
+ }
+ else
+ {
+ pEmptyWin->bAutoHide = false;
+ pEmptyWin->nState &= ~2;
+ if ( !IsFloatingMode() )
+ {
+ // The window is not "floating", should be hidden
+ SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - releasing real Splitwindow" );
+ pWorkWin->ReleaseChild_Impl( *this );
+ Hide();
+ pEmptyWin->Actualize();
+ SAL_INFO("sfx", "SfxSplitWindow::SetFadeIn_Impl - registering empty Splitwindow" );
+ pWorkWin->RegisterChild_Impl( *pEmptyWin, eAlign )->nVisible = SfxChildVisibility::VISIBLE;
+ pWorkWin->ArrangeChildren_Impl();
+ pWorkWin->ShowChildren_Impl();
+ pWorkWin->ArrangeAutoHideWindows( this );
+ }
+ else
+ {
+ Hide();
+ pWorkWin->ArrangeAutoHideWindows( this );
+ }
+ }
+}
+
+void SfxSplitWindow::FadeOut_Impl()
+{
+ if ( pEmptyWin->aTimer.IsActive() )
+ {
+ pEmptyWin->bAutoHide = false;
+ pEmptyWin->aTimer.Stop();
+ }
+
+ SetFadeIn_Impl( false );
+}
+
+void SfxSplitWindow::FadeOut()
+{
+ FadeOut_Impl();
+ SaveConfig_Impl();
+}
+
+void SfxSplitWindow::FadeIn()
+{
+ SetFadeIn_Impl( true );
+}
+
+void SfxSplitWindow::SetActiveWindow_Impl( SfxDockingWindow* pWin )
+{
+ pActive = pWin;
+ pWorkWin->SetActiveChild_Impl( this );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/srchdlg.cxx b/sfx2/source/dialog/srchdlg.cxx
new file mode 100644
index 000000000..fdd42e7e6
--- /dev/null
+++ b/sfx2/source/dialog/srchdlg.cxx
@@ -0,0 +1,135 @@
+/* -*- 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 <srchdlg.hxx>
+#include <comphelper/string.hxx>
+
+#include <tools/debug.hxx>
+#include <unotools/viewoptions.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star::uno;
+
+
+namespace sfx2 {
+
+#define MAX_SAVE_COUNT sal_uInt16(10)
+
+
+// SearchDialog
+
+
+SearchDialog::SearchDialog(weld::Window* pWindow, const OUString& rConfigName)
+ : GenericDialogController(pWindow, "sfx/ui/searchdialog.ui", "SearchDialog")
+ , m_sConfigName(rConfigName)
+ , m_xSearchEdit(m_xBuilder->weld_combo_box("searchterm"))
+ , m_xWholeWordsBox(m_xBuilder->weld_check_button("wholewords"))
+ , m_xMatchCaseBox(m_xBuilder->weld_check_button("matchcase"))
+ , m_xWrapAroundBox(m_xBuilder->weld_check_button("wrap"))
+ , m_xBackwardsBox(m_xBuilder->weld_check_button("backwards"))
+ , m_xFindBtn(m_xBuilder->weld_button("ok"))
+{
+ // set handler
+ m_xFindBtn->connect_clicked(LINK(this, SearchDialog, FindHdl));
+ // load config: old search strings and the status of the check boxes
+ LoadConfig();
+ // the search edit should have the focus
+ m_xSearchEdit->grab_focus();
+}
+
+SearchDialog::~SearchDialog()
+{
+ SaveConfig();
+}
+
+void SearchDialog::LoadConfig()
+{
+ SvtViewOptions aViewOpt( EViewType::Dialog, m_sConfigName );
+ if ( aViewOpt.Exists() )
+ {
+ Any aUserItem = aViewOpt.GetUserItem( "UserItem" );
+ OUString sUserData;
+ if ( aUserItem >>= sUserData )
+ {
+ DBG_ASSERT( comphelper::string::getTokenCount(sUserData, ';') == 5, "invalid config data" );
+ sal_Int32 nIdx = 0;
+ OUString sSearchText = sUserData.getToken( 0, ';', nIdx );
+ m_xWholeWordsBox->set_active( o3tl::toInt32(o3tl::getToken(sUserData, 0, ';', nIdx )) == 1 );
+ m_xMatchCaseBox->set_active( o3tl::toInt32(o3tl::getToken(sUserData, 0, ';', nIdx )) == 1 );
+ m_xWrapAroundBox->set_active( o3tl::toInt32(o3tl::getToken(sUserData, 0, ';', nIdx )) == 1 );
+ m_xBackwardsBox->set_active( o3tl::toInt32(o3tl::getToken(sUserData, 0, ';', nIdx )) == 1 );
+
+ nIdx = 0;
+ while ( nIdx != -1 )
+ m_xSearchEdit->append_text(sSearchText.getToken( 0, '\t', nIdx));
+ m_xSearchEdit->set_active(0);
+ }
+ }
+ else
+ m_xWrapAroundBox->set_active(true);
+}
+
+void SearchDialog::SaveConfig()
+{
+ SvtViewOptions aViewOpt( EViewType::Dialog, m_sConfigName );
+ OUString sUserData;
+ int i = 0, nCount = std::min(m_xSearchEdit->get_count(), static_cast<int>(MAX_SAVE_COUNT));
+ for ( ; i < nCount; ++i )
+ {
+ sUserData += m_xSearchEdit->get_text(i) + "\t";
+ }
+ sUserData = comphelper::string::stripStart(sUserData, '\t') + ";" +
+ OUString::number( m_xWholeWordsBox->get_active() ? 1 : 0 ) + ";" +
+ OUString::number( m_xMatchCaseBox->get_active() ? 1 : 0 ) + ";" +
+ OUString::number( m_xWrapAroundBox->get_active() ? 1 : 0 ) + ";" +
+ OUString::number( m_xBackwardsBox->get_active() ? 1 : 0 );
+
+ Any aUserItem( sUserData );
+ aViewOpt.SetUserItem( "UserItem", aUserItem );
+}
+
+IMPL_LINK_NOARG(SearchDialog, FindHdl, weld::Button&, void)
+{
+ OUString sSrchTxt = m_xSearchEdit->get_active_text();
+ auto nPos = m_xSearchEdit->find_text(sSrchTxt);
+ if (nPos != 0)
+ {
+ if (nPos != -1)
+ m_xSearchEdit->remove(nPos);
+ m_xSearchEdit->insert_text(0, sSrchTxt);
+ }
+ m_aFindHdl.Call( *this );
+}
+
+void SearchDialog::SetFocusOnEdit()
+{
+ m_xSearchEdit->select_entry_region(0, -1);
+ m_xSearchEdit->grab_focus();
+}
+
+void SearchDialog::runAsync(const std::shared_ptr<SearchDialog>& rController)
+{
+ weld::DialogController::runAsync(rController, [=](sal_Int32 /*nResult*/){ rController->m_aCloseHdl.Call(nullptr); });
+}
+
+} // namespace sfx2
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/styfitem.cxx b/sfx2/source/dialog/styfitem.cxx
new file mode 100644
index 000000000..489c4d2df
--- /dev/null
+++ b/sfx2/source/dialog/styfitem.cxx
@@ -0,0 +1,35 @@
+/* -*- 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 <sfx2/styfitem.hxx>
+#include <unotools/resmgr.hxx>
+
+SfxStyleFamilyItem::SfxStyleFamilyItem(
+ SfxStyleFamily nFamily_, const OUString& rName, const OUString& rImage,
+ const std::pair<TranslateId, SfxStyleSearchBits>* pStringArray, const std::locale& rResLocale)
+ : nFamily(nFamily_)
+ , aText(rName)
+ , aImage(rImage)
+{
+ for (const std::pair<TranslateId, SfxStyleSearchBits>* pItem = pStringArray; pItem->first;
+ ++pItem)
+ aFilterList.emplace_back(Translate::get(pItem->first, rResLocale), pItem->second);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/styledlg.cxx b/sfx2/source/dialog/styledlg.cxx
new file mode 100644
index 000000000..0e2551102
--- /dev/null
+++ b/sfx2/source/dialog/styledlg.cxx
@@ -0,0 +1,124 @@
+/* -*- 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 <svl/whiter.hxx>
+#include <svl/style.hxx>
+
+#include <sfx2/styledlg.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+
+#include "mgetempl.hxx"
+
+/* [Description]
+
+ Constructor: Add Manage TabPage, set ExampleSet from style.
+*/
+SfxStyleDialogController::SfxStyleDialogController
+(
+ weld::Window* pParent, // Parent
+ const OUString& rUIXMLDescription, const OString& rID,
+ SfxStyleSheetBase& rStyle // stylesheet to be processed
+)
+ : SfxTabDialogController(pParent, rUIXMLDescription, rID, &rStyle.GetItemSet(), true)
+ , m_rStyle(rStyle)
+{
+ // without ParentSupport suppress the standardButton
+ if (!rStyle.HasParentSupport())
+ RemoveStandardButton();
+
+ AddTabPage("organizer", SfxManageStyleSheetPage::Create, nullptr);
+
+ // With new template always set the management page as the current page
+ if (rStyle.GetName().isEmpty())
+ SetCurPageId("organizer");
+ else
+ {
+ OUString sTxt = m_xDialog->get_title() + ": " + rStyle.GetName();
+ m_xDialog->set_title(sTxt);
+ }
+ m_xExampleSet.reset(&m_rStyle.GetItemSet()); // in SfxTabDialog::Ctor() already created, reset will delete it
+
+ GetCancelButton().connect_clicked(LINK(this, SfxStyleDialogController, CancelHdl));
+}
+
+/* [Description]
+
+ Destructor: set ExampleSet to NULL, so that SfxTabDialog does not delete
+ the Set from Style.
+*/
+SfxStyleDialogController::~SfxStyleDialogController()
+{
+ m_xExampleSet.release();
+}
+
+/* [Description]
+
+ Override so that always RET_OK is returned.
+*/
+short SfxStyleDialogController::Ok()
+{
+ SfxTabDialogController::Ok();
+ return RET_OK;
+}
+
+/* [Description]
+
+ If the dialogue was canceled, then all selected attributes must be reset
+ again.
+*/
+IMPL_LINK_NOARG(SfxStyleDialogController, CancelHdl, weld::Button&, void)
+{
+ SfxTabPage* pPage = GetTabPage("organizer");
+
+ const SfxItemSet* pInSet = GetInputSetImpl();
+ SfxWhichIter aIter(*pInSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while (nWhich)
+ {
+ SfxItemState eState = aIter.GetItemState(false);
+
+ if (SfxItemState::DEFAULT == eState)
+ m_xExampleSet->ClearItem(nWhich);
+ else
+ m_xExampleSet->Put(pInSet->Get(nWhich));
+ nWhich = aIter.NextWhich();
+ }
+
+ if (pPage)
+ pPage->Reset(GetInputSetImpl());
+
+ m_xDialog->response(RET_CANCEL);
+}
+
+OUString SfxStyleDialogController::GenerateUnusedName(SfxStyleSheetBasePool &rPool, SfxStyleFamily eFam)
+{
+ OUString aNo(SfxResId(STR_NONAME));
+ sal_uInt16 i = 1;
+ OUString aNoName = aNo + OUString::number(i);
+ while (rPool.Find(aNoName, eFam))
+ {
+ ++i;
+ aNoName = aNo + OUString::number(i);
+ }
+ return aNoName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/tabdlg.cxx b/sfx2/source/dialog/tabdlg.cxx
new file mode 100644
index 000000000..11a43a498
--- /dev/null
+++ b/sfx2/source/dialog/tabdlg.cxx
@@ -0,0 +1,1160 @@
+/* -*- 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 <stdlib.h>
+#include <algorithm>
+#include <string_view>
+
+#include <sfx2/tabdlg.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/viewsh.hxx>
+#include <unotools/viewoptions.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/lok.hxx>
+
+#include <sfx2/strings.hrc>
+#include <helpids.h>
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral USERITEM_NAME = u"UserItem";
+
+
+struct TabPageImpl
+{
+ bool mbStandard;
+ SfxOkDialogController* mpSfxDialogController;
+ css::uno::Reference< css::frame::XFrame > mxFrame;
+
+ TabPageImpl() : mbStandard(false), mpSfxDialogController(nullptr) {}
+};
+
+namespace {
+
+struct Data_Impl
+{
+ OString sId; // The ID
+ CreateTabPage fnCreatePage; // Pointer to Factory
+ GetTabPageRanges fnGetRanges; // Pointer to Ranges-Function
+ std::unique_ptr<SfxTabPage> xTabPage; // The TabPage itself
+ bool bRefresh; // Flag: Page must be re-initialized
+
+ // Constructor
+ Data_Impl( const OString& rId, CreateTabPage fnPage,
+ GetTabPageRanges fnRanges ) :
+
+ sId ( rId ),
+ fnCreatePage( fnPage ),
+ fnGetRanges ( fnRanges ),
+ bRefresh ( false )
+ {
+ }
+};
+
+}
+
+SfxTabDialogItem::SfxTabDialogItem( const SfxTabDialogItem& rAttr, SfxItemPool* pItemPool )
+ : SfxSetItem( rAttr, pItemPool )
+{
+}
+
+SfxTabDialogItem::SfxTabDialogItem( sal_uInt16 nId, const SfxItemSet& rItemSet )
+ : SfxSetItem( nId, rItemSet )
+{
+}
+
+SfxTabDialogItem* SfxTabDialogItem::Clone(SfxItemPool* pToPool) const
+{
+ return new SfxTabDialogItem( *this, pToPool );
+}
+
+typedef std::vector<Data_Impl*> SfxTabDlgData_Impl;
+
+struct TabDlg_Impl
+{
+ bool bHideResetBtn : 1;
+ bool bStarted : 1;
+ SfxTabDlgData_Impl aData;
+
+ explicit TabDlg_Impl(sal_uInt8 nCnt)
+ : bHideResetBtn(false)
+ , bStarted(false)
+ {
+ aData.reserve( nCnt );
+ }
+};
+
+static Data_Impl* Find( const SfxTabDlgData_Impl& rArr, std::string_view rId, sal_uInt16* pPos = nullptr)
+{
+ const sal_uInt16 nCount = rArr.size();
+
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ {
+ Data_Impl* pObj = rArr[i];
+
+ if ( pObj->sId == rId )
+ {
+ if ( pPos )
+ *pPos = i;
+ return pObj;
+ }
+ }
+ return nullptr;
+}
+
+void SfxTabPage::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ if (pImpl)
+ pImpl->mxFrame = xFrame;
+}
+
+css::uno::Reference< css::frame::XFrame > SfxTabPage::GetFrame() const
+{
+ if (pImpl)
+ return pImpl->mxFrame;
+ return css::uno::Reference< css::frame::XFrame >();
+}
+
+SfxTabPage::SfxTabPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, const OString& rID, const SfxItemSet *rAttrSet)
+ : BuilderPage(pPage, pController, rUIXMLDescription, rID,
+ comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current()
+ && SfxViewShell::Current()->isLOKMobilePhone())
+ , pSet ( rAttrSet )
+ , bHasExchangeSupport ( false )
+ , pImpl ( new TabPageImpl )
+{
+ pImpl->mpSfxDialogController = dynamic_cast<SfxOkDialogController*>(m_pDialogController);
+}
+
+SfxTabPage::~SfxTabPage()
+{
+ if (m_xContainer)
+ {
+ std::unique_ptr<weld::Container> xParent(m_xContainer->weld_parent());
+ if (xParent)
+ xParent->move(m_xContainer.get(), nullptr);
+ }
+ m_xContainer.reset();
+ pImpl.reset();
+ m_xBuilder.reset();
+}
+
+bool SfxTabPage::FillItemSet( SfxItemSet* )
+{
+ return false;
+}
+
+void SfxTabPage::Reset( const SfxItemSet* )
+{
+}
+
+bool SfxTabPage::DeferResetToFirstActivation() { return false; }
+
+void SfxTabPage::ActivatePage( const SfxItemSet& )
+/* [Description]
+
+ Default implementation of the virtual ActivatePage method. This method is
+ called when a page of dialogue supports the exchange of data between pages.
+ <SfxTabPage::DeactivatePage(SfxItemSet *)>
+*/
+{
+}
+
+DeactivateRC SfxTabPage::DeactivatePage( SfxItemSet* )
+
+/* [Description]
+
+ Default implementation of the virtual DeactivatePage method. This method is
+ called by Sfx when leaving a page; the application can, through the return
+ value, control whether to leave the page. If the page is displayed through
+ bHasExchangeSupport which supports data exchange between pages, then a
+ pointer to the exchange set is passed as parameter. This takes on data for
+ the exchange, then the set is available as a parameter in
+ <SfxTabPage::ActivatePage(const SfxItemSet &)>.
+
+ [Return value]
+
+ DeactivateRC::LeavePage; Allow leaving the page
+*/
+
+{
+ return DeactivateRC::LeavePage;
+}
+
+
+void SfxTabPage::FillUserData()
+
+/* [Description]
+
+ Virtual method is called by the base class in the destructor to save
+ specific information of the TabPage in the ini-file. When overriding a
+ string must be compiled, which is then flushed with the <SetUserData()>.
+*/
+
+{
+}
+
+
+bool SfxTabPage::IsReadOnly() const
+{
+ return false;
+}
+
+
+const SfxPoolItem* SfxTabPage::GetItem( const SfxItemSet& rSet, sal_uInt16 nSlot, bool bDeep )
+
+/* [Description]
+
+ static Method: hereby are the implementations of the TabPage code
+ being simplified.
+*/
+
+{
+ const SfxItemPool* pPool = rSet.GetPool();
+ sal_uInt16 nWh = pPool->GetWhich( nSlot, bDeep );
+ const SfxPoolItem* pItem = nullptr;
+ rSet.GetItemState( nWh, true, &pItem );
+
+ if ( !pItem && nWh != nSlot )
+ pItem = &pPool->GetDefaultItem( nWh );
+ return pItem;
+}
+
+
+const SfxPoolItem* SfxTabPage::GetOldItem( const SfxItemSet& rSet,
+ sal_uInt16 nSlot, bool bDeep )
+
+/* [Description]
+
+ This method returns an attribute for comparison of the old value.
+*/
+
+{
+ const SfxItemSet& rOldSet = GetItemSet();
+ sal_uInt16 nWh = GetWhich( nSlot, bDeep );
+ const SfxPoolItem* pItem = nullptr;
+
+ if ( pImpl->mbStandard && rOldSet.GetParent() )
+ pItem = GetItem( *rOldSet.GetParent(), nSlot );
+ else if ( rSet.GetParent() &&
+ SfxItemState::DONTCARE == rSet.GetItemState( nWh ) )
+ pItem = GetItem( *rSet.GetParent(), nSlot );
+ else
+ pItem = GetItem( rOldSet, nSlot );
+ return pItem;
+}
+
+void SfxTabPage::PageCreated( const SfxAllItemSet& /*aSet*/ )
+{
+ SAL_WARN( "sfx.dialog", "SfxTabPage::PageCreated should not be called");
+}
+
+void SfxTabPage::ChangesApplied()
+{
+}
+
+void SfxTabPage::SetDialogController(SfxOkDialogController* pDialog)
+{
+ pImpl->mpSfxDialogController = pDialog;
+ m_pDialogController = pImpl->mpSfxDialogController;
+}
+
+SfxOkDialogController* SfxTabPage::GetDialogController() const
+{
+ return pImpl->mpSfxDialogController;
+}
+
+OString SfxTabPage::GetHelpId() const
+{
+ if (m_xContainer)
+ return m_xContainer->get_help_id();
+ return OString();
+}
+
+weld::Window* SfxTabPage::GetFrameWeld() const
+{
+ if (m_pDialogController)
+ return m_pDialogController->getDialog();
+ return nullptr;
+}
+
+const SfxItemSet* SfxTabPage::GetDialogExampleSet() const
+{
+ if (pImpl->mpSfxDialogController)
+ return pImpl->mpSfxDialogController->GetExampleSet();
+ return nullptr;
+}
+
+SfxTabDialogController::SfxTabDialogController
+(
+ weld::Widget* pParent, // Parent Window
+ const OUString& rUIXMLDescription, const OString& rID, // Dialog .ui path, Dialog Name
+ const SfxItemSet* pItemSet, // Itemset with the data;
+ // can be NULL, when Pages are onDemand
+ bool bEditFmt // when yes -> additional Button for standard
+)
+ : SfxOkDialogController(pParent, rUIXMLDescription, rID)
+ , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , m_xApplyBtn(m_xBuilder->weld_button("apply"))
+ , m_xUserBtn(m_xBuilder->weld_button("user"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xResetBtn(m_xBuilder->weld_button("reset"))
+ , m_xBaseFmtBtn(m_xBuilder->weld_button("standard"))
+ , m_pSet(pItemSet ? new SfxItemSet(*pItemSet) : nullptr)
+ , m_bStandardPushed(false)
+{
+ m_pImpl.reset(new TabDlg_Impl(m_xTabCtrl->get_n_pages()));
+ m_pImpl->bHideResetBtn = !m_xResetBtn->get_visible();
+ m_xOKBtn->connect_clicked(LINK(this, SfxTabDialogController, OkHdl));
+ m_xCancelBtn->connect_clicked(LINK(this, SfxTabDialogController, CancelHdl));
+ m_xResetBtn->connect_clicked(LINK(this, SfxTabDialogController, ResetHdl));
+ m_xResetBtn->set_label(SfxResId(STR_RESET));
+ m_xTabCtrl->connect_enter_page(LINK(this, SfxTabDialogController, ActivatePageHdl));
+ m_xTabCtrl->connect_leave_page(LINK(this, SfxTabDialogController, DeactivatePageHdl));
+ m_xResetBtn->set_help_id(HID_TABDLG_RESET_BTN);
+
+ if (bEditFmt)
+ {
+ m_xBaseFmtBtn->set_label(SfxResId(STR_STANDARD_SHORTCUT));
+ m_xBaseFmtBtn->connect_clicked(LINK(this, SfxTabDialogController, BaseFmtHdl));
+ m_xBaseFmtBtn->set_help_id(HID_TABDLG_STANDARD_BTN);
+ m_xBaseFmtBtn->show();
+ }
+
+ if (m_xUserBtn)
+ m_xUserBtn->connect_clicked(LINK(this, SfxTabDialogController, UserHdl));
+
+ if (m_pSet)
+ {
+ m_xExampleSet.reset(new SfxItemSet(*m_pSet));
+ m_pOutSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges()));
+ }
+
+ // The reset functionality seems to be confusing to many; disable in LOK.
+ if (comphelper::LibreOfficeKit::isActive())
+ RemoveResetButton();
+}
+
+IMPL_LINK_NOARG(SfxTabDialogController, OkHdl, weld::Button&, void)
+
+/* [Description]
+
+ Handler of the Ok-Buttons
+ This calls the current page <SfxTabPage::DeactivatePage(SfxItemSet *)>.
+ Returns <DeactivateRC::LeavePage>, <SfxTabDialog::Ok()> is called
+ and the Dialog is ended.
+*/
+
+{
+ if (PrepareLeaveCurrentPage())
+ m_xDialog->response(Ok());
+}
+
+IMPL_LINK_NOARG(SfxTabDialogController, UserHdl, weld::Button&, void)
+
+/* [Description]
+
+ Handler of the User-Buttons
+ This calls the current page <SfxTabPage::DeactivatePage(SfxItemSet *)>.
+ returns this <DeactivateRC::LeavePage> and <SfxTabDialog::Ok()> is called.
+ Then the Dialog is ended with the Return value <SfxTabDialog::Ok()>
+*/
+
+{
+ if (PrepareLeaveCurrentPage())
+ {
+ short nRet = Ok();
+ if (RET_OK == nRet)
+ nRet = RET_USER;
+ else
+ nRet = RET_CANCEL;
+ m_xDialog->response(nRet);
+ }
+}
+
+IMPL_LINK_NOARG(SfxTabDialogController, CancelHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(SfxTabDialogController, ResetHdl, weld::Button&, void)
+
+/* [Description]
+
+ Handler behind the reset button.
+ The Current Page is new initialized with their initial data, all the
+ settings that the user has made on this page are repealed.
+*/
+
+{
+ Data_Impl* pDataObject = Find(m_pImpl->aData, m_xTabCtrl->get_current_page_ident());
+ assert(pDataObject && "Id not known");
+
+ pDataObject->xTabPage->Reset(m_pSet.get());
+ // Also reset relevant items of ExampleSet and OutSet to initial state
+ if (!pDataObject->fnGetRanges)
+ return;
+
+ if (!m_xExampleSet)
+ m_xExampleSet.reset(new SfxItemSet(*m_pSet));
+
+ const SfxItemPool* pPool = m_pSet->GetPool();
+ const WhichRangesContainer& pTmpRanges = (pDataObject->fnGetRanges)();
+
+ for (const auto & rPair : pTmpRanges)
+ {
+ // Correct Range with multiple values
+ sal_uInt16 nTmp = rPair.first, nTmpEnd = rPair.second;
+ DBG_ASSERT(nTmp <= nTmpEnd, "Range is sorted the wrong way");
+
+ if (nTmp > nTmpEnd)
+ {
+ // If really sorted wrongly, then set new
+ std::swap(nTmp, nTmpEnd);
+ }
+
+ while (nTmp && nTmp <= nTmpEnd)
+ {
+ // Iterate over the Range and set the Items
+ sal_uInt16 nWh = pPool->GetWhich(nTmp);
+ const SfxPoolItem* pItem;
+ if (SfxItemState::SET == m_pSet->GetItemState(nWh, false, &pItem))
+ {
+ m_xExampleSet->Put(*pItem);
+ m_pOutSet->Put(*pItem);
+ }
+ else
+ {
+ m_xExampleSet->ClearItem(nWh);
+ m_pOutSet->ClearItem(nWh);
+ }
+ nTmp++;
+ }
+ }
+}
+
+/* [Description]
+
+ Handler behind the Standard-Button.
+ This button is available when editing style sheets. All the set attributes
+ in the edited stylesheet are deleted.
+*/
+IMPL_LINK_NOARG(SfxTabDialogController, BaseFmtHdl, weld::Button&, void)
+{
+ m_bStandardPushed = true;
+
+ Data_Impl* pDataObject = Find(m_pImpl->aData, m_xTabCtrl->get_current_page_ident());
+ assert(pDataObject && "Id not known");
+
+ if (!pDataObject->fnGetRanges)
+ return;
+
+ if (!m_xExampleSet)
+ m_xExampleSet.reset(new SfxItemSet(*m_pSet));
+
+ const SfxItemPool* pPool = m_pSet->GetPool();
+ const WhichRangesContainer& pTmpRanges = (pDataObject->fnGetRanges)();
+ SfxItemSet aTmpSet(*m_xExampleSet);
+
+ for (const auto& rPair : pTmpRanges)
+ {
+ // Correct Range with multiple values
+ sal_uInt16 nTmp = rPair.first, nTmpEnd = rPair.second;
+ DBG_ASSERT( nTmp <= nTmpEnd, "Range is sorted the wrong way" );
+
+ if ( nTmp > nTmpEnd )
+ {
+ // If really sorted wrongly, then set new
+ std::swap(nTmp, nTmpEnd);
+ }
+
+ while ( nTmp && nTmp <= nTmpEnd ) // guard against overflow
+ {
+ // Iterate over the Range and set the Items
+ sal_uInt16 nWh = pPool->GetWhich(nTmp);
+ m_xExampleSet->ClearItem(nWh);
+ aTmpSet.ClearItem(nWh);
+ // At the Outset of InvalidateItem,
+ // so that the change takes effect
+ m_pOutSet->InvalidateItem(nWh);
+ nTmp++;
+ }
+ }
+ // Set all Items as new -> the call the current Page Reset()
+ assert(pDataObject->xTabPage && "the Page is gone");
+ pDataObject->xTabPage->Reset( &aTmpSet );
+ pDataObject->xTabPage->pImpl->mbStandard = true;
+}
+
+IMPL_LINK(SfxTabDialogController, ActivatePageHdl, const OString&, rPage, void)
+
+/* [Description]
+
+ Handler that is called by StarView for switching to a different page.
+ If possible the <SfxTabPage::Reset(const SfxItemSet &)> or
+ <SfxTabPage::ActivatePage(const SfxItemSet &)> is called on the new page
+*/
+
+{
+ assert(!m_pImpl->aData.empty() && "no Pages registered");
+ Data_Impl* pDataObject = Find(m_pImpl->aData, rPage);
+ if (!pDataObject)
+ {
+ SAL_WARN("sfx.dialog", "Tab Page ID '" << rPage << "' not known, this is pretty serious and needs investigation");
+ return;
+ }
+
+ SfxTabPage* pTabPage = pDataObject->xTabPage.get();
+ if (!pTabPage)
+ return;
+
+ if (pDataObject->bRefresh)
+ pTabPage->Reset(m_pSet.get());
+ pDataObject->bRefresh = false;
+
+ if (m_xExampleSet)
+ pTabPage->ActivatePage(*m_xExampleSet);
+
+ if (pTabPage->IsReadOnly() || m_pImpl->bHideResetBtn)
+ m_xResetBtn->hide();
+ else
+ m_xResetBtn->show();
+}
+
+IMPL_LINK(SfxTabDialogController, DeactivatePageHdl, const OString&, rPage, bool)
+
+/* [Description]
+
+ Handler that is called by StarView before leaving a page.
+
+ [Cross-reference]
+
+ <SfxTabPage::DeactivatePage(SfxItemSet *)>
+*/
+
+{
+ assert(!m_pImpl->aData.empty() && "no Pages registered");
+ Data_Impl* pDataObject = Find(m_pImpl->aData, rPage);
+ if (!pDataObject)
+ {
+ SAL_WARN("sfx.dialog", "Tab Page ID not known, this is pretty serious and needs investigation");
+ return false;
+ }
+
+ SfxTabPage* pPage = pDataObject->xTabPage.get();
+ if (!pPage)
+ return true;
+
+ DeactivateRC nRet = DeactivateRC::LeavePage;
+
+ if (!m_xExampleSet && pPage->HasExchangeSupport() && m_pSet)
+ m_xExampleSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges()));
+
+ if (m_pSet)
+ {
+ SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
+
+ if (pPage->HasExchangeSupport())
+ nRet = pPage->DeactivatePage(&aTmp);
+ else
+ nRet = pPage->DeactivatePage(nullptr);
+ if ( ( DeactivateRC::LeavePage & nRet ) == DeactivateRC::LeavePage &&
+ aTmp.Count() && m_xExampleSet)
+ {
+ m_xExampleSet->Put( aTmp );
+ m_pOutSet->Put( aTmp );
+ }
+ }
+ else
+ {
+ if ( pPage->HasExchangeSupport() ) //!!!
+ {
+ if (!m_xExampleSet)
+ {
+ SfxItemPool* pPool = pPage->GetItemSet().GetPool();
+ m_xExampleSet.reset(new SfxItemSet(*pPool, GetInputRanges(*pPool)));
+ }
+ nRet = pPage->DeactivatePage(m_xExampleSet.get());
+ }
+ else
+ nRet = pPage->DeactivatePage( nullptr );
+ }
+
+ if ( nRet & DeactivateRC::RefreshSet )
+ {
+ RefreshInputSet();
+ // Flag all Pages as to be initialized as new
+
+ for (auto const& elem : m_pImpl->aData)
+ {
+ elem->bRefresh = ( elem->xTabPage.get() != pPage ); // Do not refresh own Page anymore
+ }
+ }
+ return static_cast<bool>(nRet & DeactivateRC::LeavePage);
+}
+
+bool SfxTabDialogController::PrepareLeaveCurrentPage()
+{
+ const OString sId = m_xTabCtrl->get_current_page_ident();
+ Data_Impl* pDataObject = Find(m_pImpl->aData, sId);
+ DBG_ASSERT( pDataObject, "Id not known" );
+ SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr;
+
+ bool bEnd = !pPage;
+
+ if ( pPage )
+ {
+ DeactivateRC nRet = DeactivateRC::LeavePage;
+ if ( m_pSet )
+ {
+ SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
+
+ if ( pPage->HasExchangeSupport() )
+ nRet = pPage->DeactivatePage( &aTmp );
+ else
+ nRet = pPage->DeactivatePage( nullptr );
+
+ if ( ( DeactivateRC::LeavePage & nRet ) == DeactivateRC::LeavePage
+ && aTmp.Count() )
+ {
+ m_xExampleSet->Put( aTmp );
+ m_pOutSet->Put( aTmp );
+ }
+ }
+ else
+ nRet = pPage->DeactivatePage( nullptr );
+ bEnd = nRet != DeactivateRC::KeepPage;
+ }
+
+ return bEnd;
+}
+
+const WhichRangesContainer & SfxTabDialogController::GetInputRanges(const SfxItemPool& rPool)
+
+/* [Description]
+
+ Makes the set over the range of all pages of the dialogue. Pages have the
+ static method for querying their range in AddTabPage, ie deliver their
+ sets onDemand.
+
+ [Return value]
+
+ Pointer to a null-terminated array of sal_uInt16. This array belongs to the
+ dialog and is deleted when the dialogue is destroy.
+
+ [Cross-reference]
+
+ <SfxTabDialog::AddTabPage(sal_uInt16, CreateTabPage, GetTabPageRanges, bool)>
+ <SfxTabDialog::AddTabPage(sal_uInt16, const String &, CreateTabPage, GetTabPageRanges, bool, sal_uInt16)>
+ <SfxTabDialog::AddTabPage(sal_uInt16, const Bitmap &, CreateTabPage, GetTabPageRanges, bool, sal_uInt16)>
+*/
+
+{
+ if ( m_pSet )
+ {
+ SAL_WARN( "sfx.dialog", "Set already exists!" );
+ return m_pSet->GetRanges();
+ }
+
+ if ( !m_pRanges.empty() )
+ return m_pRanges;
+ SfxItemSet aUS(const_cast<SfxItemPool&>(rPool));
+
+ for (auto const& elem : m_pImpl->aData)
+ {
+
+ if ( elem->fnGetRanges )
+ {
+ const WhichRangesContainer& pTmpRanges = (elem->fnGetRanges)();
+
+ for (const auto & rPair : pTmpRanges)
+ {
+ sal_uInt16 nWidFrom = rPool.GetWhich(rPair.first);
+ sal_uInt16 nWidTo = rPool.GetWhich(rPair.second);
+ aUS.MergeRange(nWidFrom, nWidTo); // Keep it valid
+ }
+ }
+ }
+
+ m_pRanges = aUS.GetRanges();
+ return m_pRanges;
+}
+
+SfxTabDialogController::~SfxTabDialogController()
+{
+ SavePosAndId();
+
+ for (auto & elem : m_pImpl->aData)
+ {
+ if ( elem->xTabPage )
+ {
+ // save settings of all pages (user data)
+ elem->xTabPage->FillUserData();
+ OUString aPageData( elem->xTabPage->GetUserData() );
+ if ( !aPageData.isEmpty() )
+ {
+ // save settings of all pages (user data)
+ OUString sConfigId = OStringToOUString(elem->xTabPage->GetConfigId(),
+ RTL_TEXTENCODING_UTF8);
+ SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
+ aPageOpt.SetUserItem( USERITEM_NAME, Any( aPageData ) );
+ }
+
+ elem->xTabPage.reset();
+ }
+ delete elem;
+ elem = nullptr;
+ }
+}
+
+short SfxTabDialogController::Ok()
+
+/* [Description]
+
+ Ok handler for the Dialogue.
+
+ Dialog's current location and current page are saved for the next time
+ the dialog is shown.
+
+ The OutputSet is created and for each page this or the special OutputSet
+ is set by calling the method <SfxTabPage::FillItemSet(SfxItemSet &)>, to
+ insert the entered data by the user into the set.
+
+ [Return value]
+
+ RET_OK: if at least one page has returned from FillItemSet,
+ otherwise RET_CANCEL.
+*/
+{
+ SavePosAndId(); //See fdo#38828 "Apply" resetting window position
+
+ if ( !m_pOutSet )
+ {
+ if ( m_xExampleSet )
+ m_pOutSet.reset(new SfxItemSet( *m_xExampleSet ));
+ else if ( m_pSet )
+ m_pOutSet = m_pSet->Clone( false ); // without Items
+ }
+ bool bModified = false;
+
+ for (auto const& elem : m_pImpl->aData)
+ {
+ SfxTabPage* pTabPage = elem->xTabPage.get();
+
+ if ( pTabPage )
+ {
+ if ( m_pSet && !pTabPage->HasExchangeSupport() )
+ {
+ SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
+
+ if ( pTabPage->FillItemSet( &aTmp ) )
+ {
+ bModified = true;
+ if (m_xExampleSet)
+ m_xExampleSet->Put( aTmp );
+ m_pOutSet->Put( aTmp );
+ }
+ }
+ }
+ }
+
+ if (m_pOutSet && m_pOutSet->Count() > 0)
+ bModified = true;
+
+ if (m_bStandardPushed)
+ bModified = true;
+
+ return bModified ? RET_OK : RET_CANCEL;
+}
+
+void SfxTabDialogController::RefreshInputSet()
+
+/* [Description]
+
+ Default implementation of the virtual Method.
+ This is called, when <SfxTabPage::DeactivatePage(SfxItemSet *)>
+ returns <DeactivateRC::RefreshSet>.
+*/
+
+{
+ SAL_INFO ( "sfx.dialog", "RefreshInputSet not implemented" );
+}
+
+void SfxTabDialogController::PageCreated
+
+/* [Description]
+
+ Default implementation of the virtual method. This is called immediately
+ after creating a page. Here the dialogue can call the TabPage Method
+ directly.
+*/
+
+(
+ const OString&, // Id of the created page
+ SfxTabPage& // Reference to the created page
+)
+{
+}
+
+void SfxTabDialogController::SavePosAndId()
+{
+ // save settings (screen position and current page)
+ SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+ aDlgOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
+}
+
+/*
+ Adds a page to the dialog. The Name must correspond to an entry in the
+ TabControl in the dialog .ui
+*/
+void SfxTabDialogController::AddTabPage(const OString &rName /* Page ID */,
+ CreateTabPage pCreateFunc /* Pointer to the Factory Method */,
+ GetTabPageRanges pRangesFunc /* Pointer to the Method for querying Ranges onDemand */)
+{
+ m_pImpl->aData.push_back(new Data_Impl(rName, pCreateFunc, pRangesFunc));
+}
+
+void SfxTabDialogController::AddTabPage(const OString &rName /* Page ID */,
+ sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */)
+{
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ CreateTabPage pCreateFunc = pFact->GetTabPageCreatorFunc(nPageCreateId);
+ GetTabPageRanges pRangesFunc = pFact->GetTabPageRangesFunc(nPageCreateId);
+ AddTabPage(rName, pCreateFunc, pRangesFunc);
+}
+
+/* [Description]
+
+ Add a page to the dialog. The Rider text is passed on, the page has no
+ counterpart in the TabControl in the resource of the dialogue.
+*/
+
+void SfxTabDialogController::AddTabPage(const OString &rName, /* Page ID */
+ const OUString& rRiderText,
+ CreateTabPage pCreateFunc /* Pointer to the Factory Method */)
+{
+ assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
+ m_xTabCtrl->append_page(rName, rRiderText);
+ AddTabPage(rName, pCreateFunc, nullptr);
+}
+
+void SfxTabDialogController::AddTabPage(const OString &rName, const OUString& rRiderText,
+ sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */)
+{
+ assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
+ m_xTabCtrl->append_page(rName, rRiderText);
+ AddTabPage(rName, nPageCreateId);
+}
+
+/* [Description]
+
+ Default implementation of the virtual Method.
+ This is called when pages create their sets onDemand.
+*/
+SfxItemSet* SfxTabDialogController::CreateInputItemSet(const OString&)
+{
+ SAL_WARN( "sfx.dialog", "CreateInputItemSet not implemented" );
+ m_xItemSet = std::make_unique<SfxAllItemSet>(SfxGetpApp()->GetPool());
+ return m_xItemSet.get();
+}
+
+void SfxTabDialogController::CreatePages()
+{
+ for (auto pDataObject : m_pImpl->aData)
+ {
+ if (pDataObject->xTabPage)
+ continue;
+ weld::Container* pPage = m_xTabCtrl->get_page(pDataObject->sId);
+ if (m_pSet)
+ pDataObject->xTabPage = (pDataObject->fnCreatePage)(pPage, this, m_pSet.get());
+ else
+ pDataObject->xTabPage = (pDataObject->fnCreatePage)(pPage, this, CreateInputItemSet(pDataObject->sId));
+ pDataObject->xTabPage->SetDialogController(this);
+ OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(), RTL_TEXTENCODING_UTF8);
+ SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
+ OUString sUserData;
+ Any aUserItem = aPageOpt.GetUserItem(USERITEM_NAME);
+ OUString aTemp;
+ if ( aUserItem >>= aTemp )
+ sUserData = aTemp;
+ pDataObject->xTabPage->SetUserData(sUserData);
+
+ PageCreated(pDataObject->sId, *pDataObject->xTabPage);
+ if (pDataObject->xTabPage->DeferResetToFirstActivation())
+ pDataObject->bRefresh = true; // Reset will be called in ActivatePageHdl
+ else
+ pDataObject->xTabPage->Reset(m_pSet.get());
+ }
+}
+
+void SfxTabDialogController::setPreviewsToSamePlace()
+{
+ //where tab pages have the same basic layout with a preview on the right,
+ //get both of their non-preview areas to request the same size so that the
+ //preview appears in the same place in each one so flipping between tabs
+ //isn't distracting as it jumps around
+ std::vector<std::unique_ptr<weld::Widget>> aGrids;
+ for (auto pDataObject : m_pImpl->aData)
+ {
+ if (!pDataObject->xTabPage)
+ continue;
+ if (!pDataObject->xTabPage->m_xBuilder)
+ continue;
+ std::unique_ptr<weld::Widget> pGrid = pDataObject->xTabPage->m_xBuilder->weld_widget("maingrid");
+ if (!pGrid)
+ continue;
+ aGrids.emplace_back(std::move(pGrid));
+ }
+
+ m_xSizeGroup.reset();
+
+ if (aGrids.size() <= 1)
+ return;
+
+ m_xSizeGroup = m_xBuilder->create_size_group();
+ m_xSizeGroup->set_mode(VclSizeGroupMode::Both);
+ for (auto& rGrid : aGrids)
+ m_xSizeGroup->add_widget(rGrid.get());
+}
+
+void SfxTabDialogController::RemoveTabPage(const OString& rId)
+
+/* [Description]
+
+ Delete the TabPage with ID nId
+*/
+
+{
+ sal_uInt16 nPos = 0;
+ m_xTabCtrl->remove_page(rId);
+ Data_Impl* pDataObject = Find( m_pImpl->aData, rId, &nPos );
+
+ if ( pDataObject )
+ {
+ if ( pDataObject->xTabPage )
+ {
+ pDataObject->xTabPage->FillUserData();
+ OUString aPageData( pDataObject->xTabPage->GetUserData() );
+ if ( !aPageData.isEmpty() )
+ {
+ // save settings of this page (user data)
+ OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(),
+ RTL_TEXTENCODING_UTF8);
+ SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
+ aPageOpt.SetUserItem( USERITEM_NAME, Any( aPageData ) );
+ }
+
+ pDataObject->xTabPage.reset();
+ }
+
+ delete pDataObject;
+ m_pImpl->aData.erase( m_pImpl->aData.begin() + nPos );
+ }
+ else
+ {
+ SAL_INFO( "sfx.dialog", "TabPage-Id not known" );
+ }
+}
+
+void SfxTabDialogController::Start_Impl()
+{
+ CreatePages();
+
+ setPreviewsToSamePlace();
+
+ assert(m_pImpl->aData.size() == static_cast<size_t>(m_xTabCtrl->get_n_pages())
+ && "not all pages registered");
+
+ // load old settings, when exists, setting SetCurPageId will override the settings,
+ // something that the sort dialog in calc depends on
+ if (m_sAppPageId.isEmpty())
+ {
+ SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
+ if (aDlgOpt.Exists())
+ m_xTabCtrl->set_current_page(aDlgOpt.GetPageID());
+ }
+
+ ActivatePageHdl(m_xTabCtrl->get_current_page_ident());
+
+ m_pImpl->bStarted = true;
+}
+
+void SfxTabDialogController::SetCurPageId(const OString& rIdent)
+{
+ m_sAppPageId = rIdent;
+ m_xTabCtrl->set_current_page(m_sAppPageId);
+}
+
+/* [Description]
+
+ The TabPage is activated with the specified Id.
+*/
+void SfxTabDialogController::ShowPage(const OString& rIdent)
+{
+ SetCurPageId(rIdent);
+ ActivatePageHdl(rIdent);
+}
+
+OString SfxTabDialogController::GetCurPageId() const
+{
+ return m_xTabCtrl->get_current_page_ident();
+}
+
+short SfxTabDialogController::run()
+{
+ Start_Impl();
+ return SfxDialogController::run();
+}
+
+bool SfxTabDialogController::runAsync(const std::shared_ptr<SfxTabDialogController>& rController,
+ const std::function<void(sal_Int32)>& rFunc)
+{
+ rController->Start_Impl();
+ return weld::DialogController::runAsync(rController, rFunc);
+}
+
+void SfxTabDialogController::SetInputSet( const SfxItemSet* pInSet )
+
+/* [Description]
+
+ With this method the Input-Set can subsequently be set initially or re-set.
+*/
+
+{
+ bool bSet = ( m_pSet != nullptr );
+ m_pSet.reset(pInSet ? new SfxItemSet(*pInSet) : nullptr);
+
+ if (!bSet && !m_xExampleSet && !m_pOutSet && m_pSet)
+ {
+ m_xExampleSet.reset(new SfxItemSet(*m_pSet));
+ m_pOutSet.reset(new SfxItemSet( *m_pSet->GetPool(), m_pSet->GetRanges() ));
+ }
+}
+
+SfxItemSet* SfxTabDialogController::GetInputSetImpl()
+
+/* [Description]
+
+ Derived classes may create new storage for the InputSet. This has to be
+ released in the Destructor. To do this, this method must be called.
+*/
+
+{
+ return m_pSet.get();
+}
+
+void SfxTabDialogController::RemoveResetButton()
+{
+ m_xResetBtn->hide();
+ m_pImpl->bHideResetBtn = true;
+}
+
+void SfxTabDialogController::RemoveStandardButton()
+{
+ m_xBaseFmtBtn->hide();
+}
+
+SfxTabPage* SfxTabDialogController::GetTabPage(std::string_view rPageId) const
+
+/* [Description]
+
+ Return TabPage with the specified Id.
+*/
+
+{
+ Data_Impl* pDataObject = Find(m_pImpl->aData, rPageId);
+ if (pDataObject)
+ return pDataObject->xTabPage.get();
+ return nullptr;
+}
+
+void SfxTabDialogController::SetApplyHandler(const Link<weld::Button&, void>& _rHdl)
+{
+ DBG_ASSERT( m_xApplyBtn, "SfxTabDialog::GetApplyHandler: no apply button enabled!" );
+ if (m_xApplyBtn)
+ m_xApplyBtn->connect_clicked(_rHdl);
+}
+
+bool SfxTabDialogController::Apply()
+{
+ bool bApplied = false;
+ if (PrepareLeaveCurrentPage())
+ {
+ bApplied = (Ok() == RET_OK);
+ //let the pages update their saved values
+ GetInputSetImpl()->Put(*GetOutputItemSet());
+ for (auto pDataObject : m_pImpl->aData)
+ {
+ if (!pDataObject->xTabPage)
+ continue;
+ pDataObject->xTabPage->ChangesApplied();
+ }
+ }
+ return bApplied;
+}
+
+std::vector<OString> SfxTabDialogController::getAllPageUIXMLDescriptions() const
+{
+ int nPages = m_xTabCtrl->get_n_pages();
+ std::vector<OString> aRet;
+ aRet.reserve(nPages);
+ for (int i = 0; i < nPages; ++i)
+ aRet.push_back(m_xTabCtrl->get_page_ident(i));
+ return aRet;
+}
+
+bool SfxTabDialogController::selectPageByUIXMLDescription(const OString& rUIXMLDescription)
+{
+ ShowPage(rUIXMLDescription);
+ return m_xTabCtrl->get_current_page_ident() == rUIXMLDescription;
+}
+
+BitmapEx SfxTabDialogController::createScreenshot() const
+{
+ // if we haven't run Start_Impl yet, do so now to create the initial pages
+ if (!m_pImpl->bStarted)
+ {
+ const_cast<SfxTabDialogController*>(this)->Start_Impl();
+ }
+
+ VclPtr<VirtualDevice> xDialogSurface(m_xDialog->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OString SfxTabDialogController::GetScreenshotId() const
+{
+ const OString sId = m_xTabCtrl->get_current_page_ident();
+ Data_Impl* pDataObject = Find(m_pImpl->aData, sId);
+ SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr;
+ if (pPage)
+ {
+ OString sHelpId(pPage->GetHelpId());
+ if (!sHelpId.isEmpty())
+ return sHelpId;
+ }
+ return m_xDialog->get_help_id();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/templdlg.cxx b/sfx2/source/dialog/templdlg.cxx
new file mode 100644
index 000000000..e1db058db
--- /dev/null
+++ b/sfx2/source/dialog/templdlg.cxx
@@ -0,0 +1,909 @@
+/* -*- 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 <vcl/commandinfoprovider.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/style.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.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 <tools/diagnose_ex.h>
+#include <sfx2/app.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/templdlg.hxx>
+#include <templdgi.hxx>
+#include <sfx2/styfitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <sfx2/sfxresid.hxx>
+
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/strings.hrc>
+#include <helpids.h>
+#include <sfx2/viewfrm.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!
+};
+
+sal_Int8 SfxCommonTemplateDialog_Impl::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ // handle drop of content into the treeview to create a new style
+ m_aStyleListExecuteDrop.Call(rEvt);
+ return DND_ACTION_NONE;
+}
+
+IMPL_LINK(SfxCommonTemplateDialog_Impl, OnAsyncExecuteDrop, void*, pStyleList, void)
+{
+ StyleList* pStyle = static_cast<StyleList*>(pStyleList);
+ if (pStyle == &m_aStyleList)
+ ActionSelect("new", m_aStyleList);
+}
+
+SfxTemplatePanelControl::SfxTemplatePanelControl(SfxBindings* pBindings, weld::Widget* pParent)
+ : PanelLayout(pParent, "TemplatePanel", "sfx/ui/templatepanel.ui")
+ , pImpl(new SfxTemplateDialog_Impl(pBindings, this))
+{
+ OSL_ASSERT(pBindings!=nullptr);
+}
+
+SfxTemplatePanelControl::~SfxTemplatePanelControl()
+{
+}
+
+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;
+ }
+ }
+}
+
+void SfxCommonTemplateDialog_Impl::connect_stylelist_execute_drop(
+ const Link<const ExecuteDropEvent&, sal_Int8>& rLink)
+{
+ m_aStyleListExecuteDrop = rLink;
+}
+
+void SfxCommonTemplateDialog_Impl::connect_stylelist_has_selected_style(const Link<void*, bool>& rLink)
+{
+ m_aStyleListHasSelectedStyle = rLink;
+}
+
+void SfxCommonTemplateDialog_Impl::connect_stylelist_update_style_dependents(const Link<void*, void>& rLink)
+{
+ m_aStyleListUpdateStyleDependents = rLink;
+}
+
+void SfxCommonTemplateDialog_Impl::connect_stylelist_enable_tree_drag(const Link<bool, void> rLink)
+{
+ m_aStyleListEnableTreeDrag = rLink;
+}
+
+void SfxCommonTemplateDialog_Impl::connect_stylelist_enable_delete(const Link<void*, void> rLink)
+{
+ m_aStyleListEnableDelete = rLink;
+}
+
+void SfxCommonTemplateDialog_Impl::connect_stylelist_set_water_can_state(
+ const Link<const SfxBoolItem*, void> rLink)
+{
+ m_aStyleListSetWaterCanState = rLink;
+}
+
+// Constructor
+
+SfxCommonTemplateDialog_Impl::SfxCommonTemplateDialog_Impl(SfxBindings* pB, weld::Container* pC, weld::Builder* pBuilder)
+ : pBindings(pB)
+ , mpContainer(pC)
+ , xModuleManager(frame::ModuleManager::create(::comphelper::getProcessComponentContext()))
+ , m_pDeletionWatcher(nullptr)
+ , m_aStyleList(pBuilder, pB, this, pC, "treeview", "flatview")
+ , mxPreviewCheckbox(pBuilder->weld_check_button("showpreview"))
+ , mxFilterLb(pBuilder->weld_combo_box("filter"))
+ , nActFamily(0xffff)
+ , nActFilter(0)
+ , bIsWater(false)
+ , bUpdate(false)
+ , bWaterDisabled(false)
+ , bNewByExampleDisabled(false)
+ , bUpdateByExampleDisabled(false)
+ , m_bWantHierarchical(false)
+{
+ mxFilterLb->set_help_id(HID_TEMPLATE_FILTER);
+ mxPreviewCheckbox->set_active(officecfg::Office::Common::StylesAndFormatting::Preview::get());
+}
+
+void SfxTemplateDialog_Impl::EnableEdit(bool bEnable, StyleList* rStyleList)
+{
+ if(rStyleList == &m_aStyleList || rStyleList == nullptr)
+ SfxCommonTemplateDialog_Impl::EnableEdit( bEnable, &m_aStyleList );
+ if( !bEnable || !bUpdateByExampleDisabled )
+ EnableItem("update", bEnable);
+}
+
+IMPL_LINK(SfxCommonTemplateDialog_Impl, ReadResource_Hdl, StyleList&, rStyleList, void)
+{
+ nActFilter = 0xffff;
+
+ SfxViewFrame* pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame();
+ SfxObjectShell* pCurObjShell = pViewFrame->GetObjectShell();
+ if (pCurObjShell)
+ {
+ nActFilter = static_cast<sal_uInt16>(LoadFactoryStyleFilter_Hdl(pCurObjShell));
+ if (0xffff == nActFilter)
+ {
+ nActFilter = pCurObjShell->GetAutoStyleFilterIndex();
+ }
+ }
+
+ size_t nCount = m_aStyleListReadResource.Call(nullptr);
+
+// 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 = rStyleList.GetFamilyItemByIndex( nCount );
+ sal_uInt16 nId = SfxTemplate::SfxFamilyIdToNId( rItem.GetFamily() );
+ InsertFamilyItem(nId, rItem);
+ }
+}
+
+IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, ClearResource_Hdl, void*, void)
+{
+ ClearFamilyList();
+ m_aStyleListClear.Call(nullptr);
+}
+
+SfxCommonTemplateDialog_Impl::DeletionWatcher *
+SfxCommonTemplateDialog_Impl::impl_setDeletionWatcher(
+ DeletionWatcher *const pNewWatcher)
+{
+ DeletionWatcher *const pRet(m_pDeletionWatcher);
+ m_pDeletionWatcher = pNewWatcher;
+ return pRet;
+}
+
+void SfxCommonTemplateDialog_Impl::Initialize()
+{
+ m_aStyleList.connect_ReadResource(LINK(this, SfxCommonTemplateDialog_Impl, ReadResource_Hdl));
+ m_aStyleList.connect_ClearResource(LINK(this, SfxCommonTemplateDialog_Impl, ClearResource_Hdl));
+ m_aStyleList.connect_LoadFactoryStyleFilter(LINK(this, SfxCommonTemplateDialog_Impl, LoadFactoryStyleFilter_Hdl));
+ m_aStyleList.connect_SaveSelection(LINK(this, SfxCommonTemplateDialog_Impl, SaveSelection_Hdl));
+ m_aStyleList.connect_UpdateFamily(LINK(this, SfxCommonTemplateDialog_Impl, UpdateFamily_Hdl));
+ m_aStyleList.connect_UpdateStyles(LINK(this, SfxCommonTemplateDialog_Impl, UpdateStyles_Hdl));
+
+ mxFilterLb->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FilterSelectHdl));
+ mxPreviewCheckbox->connect_toggled(LINK(this, SfxCommonTemplateDialog_Impl, PreviewHdl));
+ m_aStyleList.Initialize();
+}
+
+IMPL_LINK(SfxCommonTemplateDialog_Impl, UpdateStyles_Hdl, StyleFlags, nFlags, void)
+{
+ const SfxStyleFamilyItem* pItem = m_aStyleList.GetFamilyItem();
+
+ 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;
+ m_aStyleList.FilterSelect(nActFilter, false);
+ mxFilterLb->set_active(1);
+ }
+
+ // if the tree view again, select family hierarchy
+ if (m_aStyleList.IsTreeView() || m_bWantHierarchical)
+ {
+ mxFilterLb->set_active_text(SfxResId(STR_STYLE_FILTER_HIERARCHICAL));
+ EnableHierarchical(true, m_aStyleList);
+ }
+ }
+ else
+ {
+ if (nActFilter < mxFilterLb->get_count() - 1)
+ mxFilterLb->set_active(nActFilter + 1);
+ else
+ {
+ nActFilter = 0;
+ m_aStyleList.FilterSelect(nActFilter, false);
+ mxFilterLb->set_active(1);
+ }
+ }
+
+ if (!(nFlags & StyleFlags::UpdateFamilyList))
+ return;
+
+ EnableItem("watercan", false);
+}
+
+SfxCommonTemplateDialog_Impl::~SfxCommonTemplateDialog_Impl()
+{
+ if ( bIsWater )
+ Execute_Impl(SID_STYLE_WATERCAN, "", "", 0, m_aStyleList);
+ m_aStyleListClear.Call(nullptr);
+ m_aStyleListCleanup.Call(nullptr);
+ if ( m_pDeletionWatcher )
+ m_pDeletionWatcher->signal();
+ mxPreviewCheckbox.reset();
+ mxFilterLb.reset();
+}
+
+/**
+ * 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
+{
+ return m_aStyleListWaterCan.Call(nullptr);
+}
+
+void SfxCommonTemplateDialog_Impl::SelectStyle(const OUString &rStr, bool bIsCallback, StyleList& rStyleList)
+{
+ rStyleList.SelectStyle(rStr, bIsCallback);
+
+ bWaterDisabled = !IsSafeForWaterCan();
+
+ // tdf#134598 call UpdateStyleDependents to update watercan
+ UpdateStyleDependents_Hdl(nullptr);
+}
+
+void SfxCommonTemplateDialog_Impl::EnableTreeDrag(bool bEnable)
+{
+ m_aStyleListEnableTreeDrag.Call(bEnable);
+}
+
+// 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
+
+ m_aStyleListSetWaterCanState.Call(pItem);
+}
+
+// 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 )
+{
+ m_aStyleList.SetFamilyState(nSlotId, pItem);
+ bUpdate = true;
+}
+
+// Internal: Perform functions through the Dispatcher
+bool SfxCommonTemplateDialog_Impl::Execute_Impl(
+ sal_uInt16 nId, const OUString &rStr, const OUString& rRefStr, sal_uInt16 nFamily, StyleList& rStyleList,
+ 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(rStyleList.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)
+ && rStyleList.EnableExecute())
+ {
+ 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 = rStyleList.GetFamilyItem();
+ 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, StyleList& rStyleList)
+{
+ if (bEnable)
+ {
+ if (!rStyleList.IsHierarchical())
+ {
+ // Turn on treeView
+ m_bWantHierarchical = true;
+ SaveSelection_Hdl(rStyleList); // fdo#61429 store "hierarchical"
+ m_aStyleList.SetHierarchical();
+ }
+ }
+ else
+ {
+ m_aStyleList.SetFilterControlsHandle();
+ // 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, rStyleList.IsHierarchical() );
+ }
+}
+
+// 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;
+ m_aStyleList.FilterSelect(nActFilter, true);
+}
+
+void SfxCommonTemplateDialog_Impl::IsUpdate(StyleList&)
+{
+ SfxViewFrame* pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame();
+ SfxObjectShell* pDocShell = pViewFrame->GetObjectShell();
+ nActFilter = static_cast<sal_uInt16>(LoadFactoryStyleFilter_Hdl(pDocShell));
+ if (0xffff == nActFilter)
+ {
+ nActFilter = pDocShell->GetAutoStyleFilterIndex();
+ }
+}
+
+IMPL_LINK(SfxCommonTemplateDialog_Impl, FilterSelectHdl, weld::ComboBox&, rBox, void)
+{
+ if (SfxResId(STR_STYLE_FILTER_HIERARCHICAL) == rBox.get_active_text())
+ {
+ EnableHierarchical(true, m_aStyleList);
+ }
+ else
+ {
+ EnableHierarchical(false, m_aStyleList);
+ }
+}
+
+// Select-Handler for the Toolbox
+void SfxCommonTemplateDialog_Impl::FamilySelect(sal_uInt16 nEntry, StyleList&, bool bPreviewRefresh)
+{
+ assert((0 < nEntry && nEntry <= MAX_FAMILIES) || 0xffff == nEntry);
+ if( nEntry != nActFamily || bPreviewRefresh )
+ {
+ CheckItem(OString::number(nActFamily), false);
+ nActFamily = nEntry;
+ m_aStyleList.FamilySelect(nEntry);
+ }
+}
+
+void SfxCommonTemplateDialog_Impl::ActionSelect(const OString& rEntry, StyleList& rStyleList)
+{
+ if (rEntry == "watercan")
+ {
+ const bool bOldState = !IsCheckedItem(rEntry);
+ bool bCheck;
+ SfxBoolItem aBool;
+ // when a template is chosen.
+ if (!bOldState && m_aStyleListHasSelectedStyle.Call(nullptr))
+ {
+ const OUString aTemplName(rStyleList.GetSelectedEntry());
+ Execute_Impl(SID_STYLE_WATERCAN, aTemplName, "",
+ static_cast<sal_uInt16>(m_aStyleList.GetFamilyItem()->GetFamily()), rStyleList);
+ bCheck = true;
+ }
+ else
+ {
+ Execute_Impl(SID_STYLE_WATERCAN, "", "", 0, rStyleList);
+ bCheck = false;
+ }
+ CheckItem(rEntry, bCheck);
+ aBool.SetValue(bCheck);
+ SetWaterCanState(&aBool);
+ }
+ else if (rEntry == "new" || rEntry == "newmenu")
+ {
+ m_aStyleListNewMenu.Call(nullptr);
+ }
+ else if (rEntry == "update")
+ {
+ Execute_Impl(SID_STYLE_UPDATE_BY_EXAMPLE,
+ "", "",
+ static_cast<sal_uInt16>(m_aStyleList.GetFamilyItem()->GetFamily()), rStyleList);
+ }
+ 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& )
+ {
+ TOOLS_WARN_EXCEPTION( "sfx", "getModuleIdentifier(): exception of XModuleManager::identify()" );
+ }
+
+ return sIdentifier;
+}
+
+IMPL_LINK(SfxCommonTemplateDialog_Impl, LoadFactoryStyleFilter_Hdl, SfxObjectShell const*, i_pObjSh, sal_Int32)
+{
+ 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{ comphelper::makePropertyValue(
+ "ooSetupFactoryStyleFilter", i_nFilter | (m_bWantHierarchical ? 0x1000 : 0)) };
+ xModuleManager->replaceByName( getModuleIdentifier( xModuleManager, i_pObjSh ), Any( lProps ) );
+}
+
+IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, SaveSelection_Hdl, StyleList&, SfxObjectShell*)
+{
+ SfxViewFrame *const pViewFrame(pBindings->GetDispatcher_Impl()->GetFrame());
+ SfxObjectShell *const pDocShell(pViewFrame->GetObjectShell());
+ if (pDocShell)
+ {
+ pDocShell->SetAutoStyleFilterIndex(nActFilter);
+ SaveFactoryStyleFilter( pDocShell, nActFilter );
+ }
+ return pDocShell;
+}
+
+IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, PreviewHdl, weld::Toggleable&, 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();
+
+ m_aStyleList.EnablePreview(bCustomPreview);
+
+ FamilySelect(nActFamily, m_aStyleList, true);
+}
+
+IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, UpdateStyleDependents_Hdl, void*, void)
+{
+ m_aStyleListUpdateStyleDependents.Call(nullptr);
+ EnableItem("watercan", !bWaterDisabled);
+ m_aStyleListEnableDelete.Call(nullptr);
+}
+
+void SfxCommonTemplateDialog_Impl::EnableExample_Impl(sal_uInt16 nId, bool bEnable)
+{
+ bool bDisable = !bEnable || !IsSafeForWaterCan();
+ if (nId == SID_STYLE_NEW_BY_EXAMPLE)
+ {
+ bNewByExampleDisabled = bDisable;
+ m_aStyleList.EnableNewByExample(bNewByExampleDisabled);
+ EnableItem("new", bEnable);
+ EnableItem("newmenu", bEnable);
+ }
+ else if( nId == SID_STYLE_UPDATE_BY_EXAMPLE )
+ {
+ bUpdateByExampleDisabled = bDisable;
+ EnableItem("update", bEnable);
+ }
+}
+
+SfxTemplateDialog_Impl::SfxTemplateDialog_Impl(SfxBindings* pB, SfxTemplatePanelControl* pDlgWindow)
+ : SfxCommonTemplateDialog_Impl(pB, pDlgWindow->get_container(), 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);
+
+}
+
+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_aStyleList);
+ 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(), m_aStyleList);
+}
+
+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, m_aStyleList);
+}
+
+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, m_aStyleList);
+}
+
+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 )
+ {
+ m_aStyleListSetFamily.Call(nId);
+ nActFamily = nId;
+ }
+}
+
+IMPL_LINK(SfxCommonTemplateDialog_Impl, UpdateFamily_Hdl, StyleList&, rStyleList, void)
+{
+ bWaterDisabled = false;
+ bUpdateByExampleDisabled = false;
+
+ if (IsCheckedItem("watercan") &&
+ // only if that area is allowed
+ rStyleList.CurrentFamilyHasState())
+ {
+ Execute_Impl(SID_STYLE_APPLY, rStyleList.GetSelectedEntry(), OUString(),
+ static_cast<sal_uInt16>(rStyleList.GetFamilyItem()->GetFamily()), rStyleList);
+ }
+}
+
+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;
+}
+
+void SfxCommonTemplateDialog_Impl::EnableEdit(bool b, StyleList* rStyleList)
+{
+ if (rStyleList == &m_aStyleList || rStyleList == nullptr)
+ m_aStyleList.Enableedit(b);
+}
+void SfxCommonTemplateDialog_Impl::EnableDel(bool b, const StyleList* rStyleList)
+{
+ if (rStyleList == &m_aStyleList || rStyleList == nullptr)
+ m_aStyleList.Enabledel(b);
+}
+void SfxCommonTemplateDialog_Impl::EnableNew(bool b, const StyleList* rStyleList)
+{
+ if (rStyleList == &m_aStyleList || rStyleList == nullptr)
+ m_aStyleList.Enablenew(b);
+}
+void SfxCommonTemplateDialog_Impl::EnableHide(bool b, const StyleList* rStyleList)
+{
+ if (rStyleList == &m_aStyleList || rStyleList == nullptr)
+ m_aStyleList.Enablehide(b);
+}
+void SfxCommonTemplateDialog_Impl::EnableShow(bool b, const StyleList* rStyleList)
+{
+ if (rStyleList == &m_aStyleList || rStyleList == nullptr)
+ m_aStyleList.Enableshow(b);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/tplcitem.cxx b/sfx2/source/dialog/tplcitem.cxx
new file mode 100644
index 000000000..c86aabcd7
--- /dev/null
+++ b/sfx2/source/dialog/tplcitem.cxx
@@ -0,0 +1,169 @@
+/* -*- 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 <svl/intitem.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <tplcitem.hxx>
+#include <templdgi.hxx>
+
+// Constructor
+
+SfxTemplateControllerItem::SfxTemplateControllerItem(
+ sal_uInt16 nSlotId, // ID
+ SfxCommonTemplateDialog_Impl &rDlg, // Controller-Instance,
+ // which is assigned to this item.
+ SfxBindings &rBindings):
+ SfxControllerItem(nSlotId, rBindings),
+ rTemplateDlg(rDlg),
+ nWaterCanState(0xff),
+ nUserEventId(nullptr)
+{
+}
+
+SfxTemplateControllerItem::~SfxTemplateControllerItem()
+{
+ if(nUserEventId)
+ Application::RemoveUserEvent(nUserEventId);
+}
+
+
+// Notice about change of status, is propagated through the Controller
+// passed on by the constructor
+
+void SfxTemplateControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pItem )
+{
+ switch(nSID)
+ {
+ case SID_STYLE_FAMILY1:
+ case SID_STYLE_FAMILY2:
+ case SID_STYLE_FAMILY3:
+ case SID_STYLE_FAMILY4:
+ case SID_STYLE_FAMILY5:
+ case SID_STYLE_FAMILY6:
+ {
+ bool bAvailable = SfxItemState::DEFAULT == eState;
+ if ( !bAvailable )
+ rTemplateDlg.SetFamilyState(GetId(), nullptr);
+ else {
+ const SfxTemplateItem *pStateItem = dynamic_cast< const SfxTemplateItem* >(pItem);
+ DBG_ASSERT(pStateItem != nullptr, "SfxTemplateItem expected");
+ rTemplateDlg.SetFamilyState( GetId(), pStateItem );
+ }
+ bool bDisable = eState == SfxItemState::DISABLED;
+ // Disable Family
+ sal_uInt16 nFamily = 0;
+ switch( GetId())
+ {
+ case SID_STYLE_FAMILY1:
+ nFamily = 1; break;
+ case SID_STYLE_FAMILY2:
+ nFamily = 2; break;
+ case SID_STYLE_FAMILY3:
+ nFamily = 3; break;
+ case SID_STYLE_FAMILY4:
+ nFamily = 4; break;
+ case SID_STYLE_FAMILY5:
+ nFamily = 5; break;
+ case SID_STYLE_FAMILY6:
+ nFamily = 6; break;
+
+ default: OSL_FAIL("unknown StyleFamily"); break;
+ }
+ rTemplateDlg.EnableFamilyItem( nFamily, !bDisable );
+ break;
+ }
+ case SID_STYLE_WATERCAN:
+ {
+ if ( eState == SfxItemState::DISABLED )
+ nWaterCanState = 0xff;
+ else if( eState == SfxItemState::DEFAULT )
+ {
+ const SfxBoolItem& rStateItem = dynamic_cast<const SfxBoolItem&>(*pItem);
+ nWaterCanState = rStateItem.GetValue() ? 1 : 0;
+ }
+ //not necessary if the last event is still on the way
+ if(!nUserEventId)
+ nUserEventId = Application::PostUserEvent( LINK(
+ this, SfxTemplateControllerItem, SetWaterCanStateHdl_Impl ) );
+ break;
+ }
+ case SID_STYLE_EDIT:
+ rTemplateDlg.EnableEdit( SfxItemState::DISABLED != eState, nullptr );
+ break;
+ case SID_STYLE_DELETE:
+ rTemplateDlg.EnableDel( SfxItemState::DISABLED != eState, nullptr );
+ break;
+ case SID_STYLE_HIDE:
+ rTemplateDlg.EnableHide( SfxItemState::DISABLED != eState, nullptr );
+ break;
+ case SID_STYLE_SHOW:
+ rTemplateDlg.EnableShow( SfxItemState::DISABLED != eState, nullptr );
+ break;
+ case SID_STYLE_NEW_BY_EXAMPLE:
+
+ rTemplateDlg.EnableExample_Impl(nSID, SfxItemState::DISABLED != eState);
+ break;
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ rTemplateDlg.EnableExample_Impl(nSID, eState != SfxItemState::DISABLED);
+ break;
+ }
+ case SID_STYLE_NEW:
+ {
+ rTemplateDlg.EnableNew( SfxItemState::DISABLED != eState, nullptr );
+ break;
+ }
+ case SID_STYLE_DRAGHIERARCHIE:
+ {
+ rTemplateDlg.EnableTreeDrag( SfxItemState::DISABLED != eState );
+ break;
+ }
+ case SID_STYLE_FAMILY :
+ {
+ const SfxUInt16Item *pStateItem = dynamic_cast< const SfxUInt16Item* >(pItem);
+ if (pStateItem)
+ {
+ rTemplateDlg.SetFamily(static_cast<SfxStyleFamily>(pStateItem->GetValue()));
+ }
+ break;
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SfxTemplateControllerItem, SetWaterCanStateHdl_Impl, void*, void)
+{
+ nUserEventId = nullptr;
+ std::unique_ptr<SfxBoolItem> pState;
+ switch(nWaterCanState)
+ {
+ case 0 :
+ case 1 :
+ pState.reset(new SfxBoolItem(SID_STYLE_WATERCAN, nWaterCanState != 0));
+ break;
+ }
+ rTemplateDlg.SetWaterCanState(pState.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/tplpitem.cxx b/sfx2/source/dialog/tplpitem.cxx
new file mode 100644
index 000000000..166263444
--- /dev/null
+++ b/sfx2/source/dialog/tplpitem.cxx
@@ -0,0 +1,90 @@
+/* -*- 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 <sfx2/tplpitem.hxx>
+#include <com/sun/star/frame/status/Template.hpp>
+
+SfxPoolItem* SfxTemplateItem::CreateDefault() { return new SfxTemplateItem; }
+
+
+SfxTemplateItem::SfxTemplateItem()
+{
+}
+
+SfxTemplateItem::SfxTemplateItem
+(
+ sal_uInt16 nWhichId, // Slot-ID
+ const OUString& rStyle, // Name of the current Styles
+ const OUString& rStyleIdentifier // Prog Name of current Style
+) : SfxFlagItem( nWhichId, static_cast<sal_uInt16>(SfxStyleSearchBits::All) ),
+ aStyle( rStyle ),
+ aStyleIdentifier( rStyleIdentifier )
+{
+}
+
+// op ==
+
+bool SfxTemplateItem::operator==( const SfxPoolItem& rCmp ) const
+{
+ return ( SfxFlagItem::operator==( rCmp ) &&
+ aStyle == static_cast<const SfxTemplateItem&>(rCmp).aStyle &&
+ aStyleIdentifier == static_cast<const SfxTemplateItem&>(rCmp).aStyleIdentifier );
+}
+
+SfxTemplateItem* SfxTemplateItem::Clone( SfxItemPool *) const
+{
+ return new SfxTemplateItem(*this);
+}
+
+bool SfxTemplateItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ css::frame::status::Template aTemplate;
+
+ aTemplate.Value = static_cast<sal_uInt16>(GetValue());
+ aTemplate.StyleName = aStyle;
+ aTemplate.StyleNameIdentifier = aStyleIdentifier;
+ rVal <<= aTemplate;
+
+ return true;
+}
+
+
+bool SfxTemplateItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ css::frame::status::Template aTemplate;
+
+ if ( rVal >>= aTemplate )
+ {
+ SetValue( static_cast<SfxStyleSearchBits>(aTemplate.Value) );
+ aStyle = aTemplate.StyleName;
+ aStyleIdentifier = aTemplate.StyleNameIdentifier;
+ return true;
+ }
+
+ return false;
+}
+
+
+sal_uInt8 SfxTemplateItem::GetFlagCount() const
+{
+ return sizeof(sal_uInt16) * 8;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/dialog/versdlg.cxx b/sfx2/source/dialog/versdlg.cxx
new file mode 100644
index 000000000..47f33b634
--- /dev/null
+++ b/sfx2/source/dialog/versdlg.cxx
@@ -0,0 +1,473 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/document/XCmisDocument.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/util/RevisionTag.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#include <officecfg/Office/Common.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemset.hxx>
+#include <unotools/useroptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <tools/datetime.hxx>
+
+#include <versdlg.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/dialoghelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/dispatch.hxx>
+
+#include <sfx2/sfxuno.hxx>
+#include <memory>
+#include <vector>
+
+using namespace com::sun::star;
+
+struct SfxVersionInfo
+{
+ OUString aName;
+ OUString aComment;
+ OUString aAuthor;
+ DateTime aCreationDate;
+
+ SfxVersionInfo();
+};
+
+class SfxVersionTableDtor
+{
+private:
+ std::vector<std::unique_ptr<SfxVersionInfo>> aTableList;
+public:
+ explicit SfxVersionTableDtor( const uno::Sequence < util::RevisionTag >& rInfo );
+ explicit SfxVersionTableDtor( const uno::Sequence < document::CmisVersion > & rInfo );
+ SfxVersionTableDtor(const SfxVersionTableDtor&) = delete;
+ SfxVersionTableDtor& operator=(const SfxVersionTableDtor&) = delete;
+
+ size_t size() const
+ { return aTableList.size(); }
+
+ SfxVersionInfo* at( size_t i ) const
+ { return aTableList[ i ].get(); }
+};
+
+SfxVersionTableDtor::SfxVersionTableDtor( const uno::Sequence < util::RevisionTag >& rInfo )
+{
+ for ( const auto& rItem : rInfo )
+ {
+ std::unique_ptr<SfxVersionInfo> pInfo(new SfxVersionInfo);
+ pInfo->aName = rItem.Identifier;
+ pInfo->aComment = rItem.Comment;
+ pInfo->aAuthor = rItem.Author;
+
+ pInfo->aCreationDate = DateTime( rItem.TimeStamp );
+ aTableList.push_back( std::move(pInfo) );
+ }
+}
+
+SfxVersionTableDtor::SfxVersionTableDtor( const uno::Sequence < document::CmisVersion >& rInfo )
+{
+ for ( const auto& rItem : rInfo )
+ {
+ std::unique_ptr<SfxVersionInfo> pInfo(new SfxVersionInfo);
+ pInfo->aName = rItem.Id;
+ pInfo->aComment = rItem.Comment;
+ pInfo->aAuthor = rItem.Author;
+
+ pInfo->aCreationDate = DateTime( rItem.TimeStamp );
+ aTableList.push_back( std::move(pInfo) );
+ }
+}
+
+SfxVersionInfo::SfxVersionInfo()
+ : aCreationDate( DateTime::EMPTY )
+{
+}
+
+namespace
+{
+ void setColSizes(weld::TreeView& rVersionBox)
+ {
+ // recalculate the datetime column width
+ int nWidestTime(rVersionBox.get_pixel_size(getWidestDateTime(Application::GetSettings().GetLocaleDataWrapper(), false)).Width());
+ int nW1 = rVersionBox.get_pixel_size(rVersionBox.get_column_title(1)).Width();
+
+ int nMax = std::max(nWidestTime, nW1) + 12; // max width + a little offset
+ const int nRest = rVersionBox.get_preferred_size().Width() - nMax;
+
+ std::set<OUString> aAuthors;
+ aAuthors.insert(SvtUserOptions().GetFullName());
+
+ for (int i = 0; i < rVersionBox.n_children(); ++i)
+ {
+ aAuthors.insert(weld::fromId<SfxVersionInfo*>(rVersionBox.get_id(i))->aAuthor);
+ }
+
+ int nMaxAuthorWidth = nRest/4;
+ for (auto const& author : aAuthors)
+ {
+ nMaxAuthorWidth = std::max<int>(nMaxAuthorWidth, rVersionBox.get_pixel_size(author).Width());
+ if (nMaxAuthorWidth > nRest/2)
+ {
+ nMaxAuthorWidth = nRest/2;
+ break;
+ }
+ }
+
+ rVersionBox.set_column_fixed_widths({ nMax, nMaxAuthorWidth });
+ }
+}
+
+SfxVersionDialog::SfxVersionDialog(weld::Window* pParent, SfxViewFrame* pVwFrame, bool bIsSaveVersionOnClose)
+ : SfxDialogController(pParent, "sfx/ui/versionsofdialog.ui", "VersionsOfDialog")
+ , m_pViewFrame(pVwFrame)
+ , m_bIsSaveVersionOnClose(bIsSaveVersionOnClose)
+ , m_xSaveButton(m_xBuilder->weld_button("save"))
+ , m_xSaveCheckBox(m_xBuilder->weld_check_button("always"))
+ , m_xOpenButton(m_xBuilder->weld_button("open"))
+ , m_xViewButton(m_xBuilder->weld_button("show"))
+ , m_xDeleteButton(m_xBuilder->weld_button("delete"))
+ , m_xCompareButton(m_xBuilder->weld_button("compare"))
+ , m_xCmisButton(m_xBuilder->weld_button("cmis"))
+ , m_xVersionBox(m_xBuilder->weld_tree_view("versions"))
+{
+ m_xVersionBox->set_size_request(m_xVersionBox->get_approximate_digit_width() * 90,
+ m_xVersionBox->get_height_rows(15));
+ setColSizes(*m_xVersionBox);
+
+ Link<weld::Button&,void> aClickLink = LINK( this, SfxVersionDialog, ButtonHdl_Impl );
+ m_xViewButton->connect_clicked( aClickLink );
+ m_xSaveButton->connect_clicked( aClickLink );
+ m_xDeleteButton->connect_clicked( aClickLink );
+ m_xCompareButton->connect_clicked( aClickLink );
+ m_xOpenButton->connect_clicked( aClickLink );
+ m_xSaveCheckBox->connect_toggled(LINK(this, SfxVersionDialog, ToggleHdl_Impl));
+ m_xCmisButton->connect_clicked( aClickLink );
+
+ m_xVersionBox->connect_changed( LINK( this, SfxVersionDialog, SelectHdl_Impl ) );
+ m_xVersionBox->connect_row_activated( LINK( this, SfxVersionDialog, DClickHdl_Impl ) );
+
+ m_xVersionBox->grab_focus();
+
+ // set dialog title (filename or docinfo title)
+ OUString sText = m_xDialog->get_title() +
+ " " + m_pViewFrame->GetObjectShell()->GetTitle();
+ m_xDialog->set_title(sText);
+
+ Init_Impl();
+}
+
+static OUString ConvertWhiteSpaces_Impl( const OUString& rText )
+{
+ // converted linebreaks and tabs to blanks; it's necessary for the display
+ OUStringBuffer sConverted;
+ const sal_Unicode* pChars = rText.getStr();
+ while ( *pChars )
+ {
+ switch ( *pChars )
+ {
+ case '\n' :
+ case '\t' :
+ sConverted.append(' ');
+ break;
+
+ default:
+ sConverted.append(*pChars);
+ }
+
+ ++pChars;
+ }
+
+ return sConverted.makeStringAndClear();
+}
+
+void SfxVersionDialog::Init_Impl()
+{
+ SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
+ SfxMedium* pMedium = pObjShell->GetMedium();
+ uno::Sequence < util::RevisionTag > aVersions = pMedium->GetVersionList( true );
+ m_pTable.reset(new SfxVersionTableDtor( aVersions ));
+ m_xVersionBox->freeze();
+ for (size_t n = 0; n < m_pTable->size(); ++n)
+ {
+ SfxVersionInfo *pInfo = m_pTable->at( n );
+ OUString aEntry = formatDateTime(pInfo->aCreationDate, Application::GetSettings().GetLocaleDataWrapper(), false);
+ m_xVersionBox->append(weld::toId(pInfo), aEntry);
+ auto nLastRow = m_xVersionBox->n_children() - 1;
+ m_xVersionBox->set_text(nLastRow, pInfo->aAuthor, 1);
+ m_xVersionBox->set_text(nLastRow, ConvertWhiteSpaces_Impl(pInfo->aComment), 2);
+ }
+ m_xVersionBox->thaw();
+
+ if (auto nCount = m_pTable->size())
+ m_xVersionBox->select(nCount - 1);
+
+ m_xSaveCheckBox->set_active(m_bIsSaveVersionOnClose);
+
+ bool bEnable = !pObjShell->IsReadOnly();
+ m_xSaveButton->set_sensitive( bEnable );
+ m_xSaveCheckBox->set_sensitive( bEnable );
+
+ m_xOpenButton->set_sensitive(false);
+ m_xViewButton->set_sensitive(false);
+ m_xDeleteButton->set_sensitive(false);
+ m_xCompareButton->set_sensitive(false);
+
+ if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
+ m_xCmisButton->hide( );
+ uno::Reference<document::XCmisDocument> xCmisDoc(pObjShell->GetModel(), uno::UNO_QUERY);
+ if (xCmisDoc && xCmisDoc->isVersionable())
+ m_xCmisButton->set_sensitive(true);
+ else
+ m_xCmisButton->set_sensitive(false);
+
+ SelectHdl_Impl(*m_xVersionBox);
+}
+
+SfxVersionDialog::~SfxVersionDialog()
+{
+}
+
+void SfxVersionDialog::Open_Impl()
+{
+ SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
+
+ auto nPos = m_xVersionBox->get_selected_index();
+ SfxInt16Item aItem( SID_VERSION, nPos + 1);
+ SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
+ SfxStringItem aReferer( SID_REFERER, "private:user" );
+ SfxStringItem aFile( SID_FILE_NAME, pObjShell->GetMedium()->GetName() );
+
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ if ( GetEncryptionData_Impl( pObjShell->GetMedium()->GetItemSet(), aEncryptionData ) )
+ {
+ // there is a password, it should be used during the opening
+ SfxUnoAnyItem aEncryptionDataItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) );
+ m_pViewFrame->GetDispatcher()->ExecuteList(
+ SID_OPENDOC, SfxCallMode::ASYNCHRON,
+ { &aFile, &aItem, &aTarget, &aReferer, &aEncryptionDataItem });
+ }
+ else
+ {
+ m_pViewFrame->GetDispatcher()->ExecuteList(
+ SID_OPENDOC, SfxCallMode::ASYNCHRON,
+ { &aFile, &aItem, &aTarget, &aReferer });
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SfxVersionDialog, DClickHdl_Impl, weld::TreeView&, bool)
+{
+ Open_Impl();
+ return true;
+}
+
+IMPL_LINK_NOARG(SfxVersionDialog, SelectHdl_Impl, weld::TreeView&, void)
+{
+ bool bEnable = m_xVersionBox->get_selected_index() != -1;
+ SfxObjectShell* pObjShell = m_pViewFrame->GetObjectShell();
+ m_xDeleteButton->set_sensitive(bEnable && !pObjShell->IsReadOnly());
+ m_xOpenButton->set_sensitive(bEnable);
+ m_xViewButton->set_sensitive(bEnable);
+
+ const SfxPoolItem *pDummy=nullptr;
+ m_pViewFrame->GetDispatcher()->QueryState( SID_DOCUMENT_MERGE, pDummy );
+ SfxItemState eState = m_pViewFrame->GetDispatcher()->QueryState( SID_DOCUMENT_COMPARE, pDummy );
+ m_xCompareButton->set_sensitive(bEnable && eState >= SfxItemState::DEFAULT);
+}
+
+IMPL_LINK(SfxVersionDialog, ButtonHdl_Impl, weld::Button&, rButton, void)
+{
+ SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
+
+ int nEntry = m_xVersionBox->get_selected_index();
+
+ if (&rButton == m_xSaveButton.get())
+ {
+ SfxVersionInfo aInfo;
+ aInfo.aAuthor = SvtUserOptions().GetFullName();
+ SfxViewVersionDialog_Impl aDlg(m_xDialog.get(), aInfo, true);
+ short nRet = aDlg.run();
+ if (nRet == RET_OK)
+ {
+ SfxStringItem aComment( SID_DOCINFO_COMMENTS, aInfo.aComment );
+ pObjShell->SetModified();
+ const SfxPoolItem* aItems[2];
+ aItems[0] = &aComment;
+ aItems[1] = nullptr;
+ m_pViewFrame->GetBindings().ExecuteSynchron( SID_SAVEDOC, aItems );
+ m_xVersionBox->freeze();
+ m_xVersionBox->clear();
+ m_xVersionBox->thaw();
+ Init_Impl();
+ }
+ }
+ else if (&rButton == m_xDeleteButton.get() && nEntry != -1)
+ {
+ SfxVersionInfo* pInfo = weld::fromId<SfxVersionInfo*>(m_xVersionBox->get_id(nEntry));
+ pObjShell->GetMedium()->RemoveVersion_Impl(pInfo->aName);
+ pObjShell->SetModified();
+ m_xVersionBox->freeze();
+ m_xVersionBox->clear();
+ m_xVersionBox->thaw();
+ Init_Impl();
+ }
+ else if (&rButton == m_xOpenButton.get() && nEntry != -1)
+ {
+ Open_Impl();
+ }
+ else if (&rButton == m_xViewButton.get() && nEntry != -1)
+ {
+ SfxVersionInfo* pInfo = weld::fromId<SfxVersionInfo*>(m_xVersionBox->get_id(nEntry));
+ SfxViewVersionDialog_Impl aDlg(m_xDialog.get(), *pInfo, false);
+ aDlg.run();
+ }
+ else if (&rButton == m_xCompareButton.get() && nEntry != -1)
+ {
+ SfxAllItemSet aSet( pObjShell->GetPool() );
+ aSet.Put(SfxInt16Item(SID_VERSION, nEntry + 1));
+ aSet.Put(SfxStringItem(SID_FILE_NAME, pObjShell->GetMedium()->GetName()));
+
+ SfxItemSet* pSet = pObjShell->GetMedium()->GetItemSet();
+ const SfxStringItem* pFilterItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_FILTER_NAME, false);
+ const SfxStringItem* pFilterOptItem = SfxItemSet::GetItem<SfxStringItem>(pSet, SID_FILE_FILTEROPTIONS, false);
+ if ( pFilterItem )
+ aSet.Put( *pFilterItem );
+ if ( pFilterOptItem )
+ aSet.Put( *pFilterOptItem );
+
+ m_pViewFrame->GetDispatcher()->Execute( SID_DOCUMENT_COMPARE, SfxCallMode::ASYNCHRON, aSet );
+ m_xDialog->response(RET_CLOSE);
+ }
+ else if (&rButton == m_xCmisButton.get())
+ {
+ SfxCmisVersionsDialog aDlg(m_xDialog.get(), m_pViewFrame);
+ aDlg.run();
+ }
+}
+
+IMPL_LINK(SfxVersionDialog, ToggleHdl_Impl, weld::Toggleable&, rButton, void)
+{
+ if (&rButton == m_xSaveCheckBox.get())
+ {
+ m_bIsSaveVersionOnClose = m_xSaveCheckBox->get_active();
+ }
+}
+
+SfxViewVersionDialog_Impl::SfxViewVersionDialog_Impl(weld::Window *pParent, SfxVersionInfo& rInfo, bool bEdit)
+ : SfxDialogController(pParent, "sfx/ui/versioncommentdialog.ui", "VersionCommentDialog")
+ , m_rInfo(rInfo)
+ , m_xDateTimeText(m_xBuilder->weld_label("timestamp"))
+ , m_xSavedByText(m_xBuilder->weld_label("author"))
+ , m_xEdit(m_xBuilder->weld_text_view("textview"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+ , m_xCancelButton(m_xBuilder->weld_button("cancel"))
+ , m_xCloseButton(m_xBuilder->weld_button("close"))
+{
+ OUString sAuthor = rInfo.aAuthor.isEmpty() ? SfxResId(STR_NO_NAME_SET) : rInfo.aAuthor;
+
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ m_xDateTimeText->set_label(m_xDateTimeText->get_label() + formatDateTime(rInfo.aCreationDate, rLocaleWrapper, false));
+ m_xSavedByText->set_label(m_xSavedByText->get_label() + sAuthor);
+ m_xEdit->set_text(rInfo.aComment);
+ m_xEdit->set_size_request(40 * m_xEdit->get_approximate_digit_width(),
+ 7 * m_xEdit->get_text_height());
+ m_xOKButton->connect_clicked(LINK(this, SfxViewVersionDialog_Impl, ButtonHdl));
+
+ if (!bEdit)
+ {
+ m_xOKButton->hide();
+ m_xCancelButton->hide();
+ m_xEdit->set_editable(false);
+ m_xDialog->set_title(SfxResId(STR_VIEWVERSIONCOMMENT));
+ m_xCloseButton->grab_focus();
+ }
+ else
+ {
+ m_xDateTimeText->hide();
+ m_xCloseButton->hide();
+ m_xEdit->grab_focus();
+ }
+}
+
+IMPL_LINK(SfxViewVersionDialog_Impl, ButtonHdl, weld::Button&, rButton, void)
+{
+ assert(&rButton == m_xOKButton.get());
+ (void)rButton;
+ m_rInfo.aComment = m_xEdit->get_text();
+ m_xDialog->response(RET_OK);
+}
+
+SfxCmisVersionsDialog::SfxCmisVersionsDialog(weld::Window* pParent, SfxViewFrame* pVwFrame)
+ : SfxDialogController(pParent, "sfx/ui/versionscmis.ui", "VersionsCmisDialog")
+ , m_pViewFrame(pVwFrame)
+ , m_xOpenButton(m_xBuilder->weld_button("open"))
+ , m_xViewButton(m_xBuilder->weld_button("show"))
+ , m_xDeleteButton(m_xBuilder->weld_button("delete"))
+ , m_xCompareButton(m_xBuilder->weld_button("compare"))
+ , m_xVersionBox(m_xBuilder->weld_tree_view("versions"))
+{
+ m_xVersionBox->set_size_request(m_xVersionBox->get_approximate_digit_width() * 90,
+ m_xVersionBox->get_height_rows(15));
+ setColSizes(*m_xVersionBox);
+
+ m_xVersionBox->grab_focus();
+
+ OUString sText = m_xDialog->get_title() +
+ " " + m_pViewFrame->GetObjectShell()->GetTitle();
+ m_xDialog->set_title(sText);
+
+ LoadVersions();
+}
+
+SfxCmisVersionsDialog::~SfxCmisVersionsDialog()
+{
+}
+
+void SfxCmisVersionsDialog::LoadVersions()
+{
+ SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
+ uno::Sequence < document::CmisVersion > aVersions = pObjShell->GetCmisVersions( );
+ m_pTable.reset(new SfxVersionTableDtor( aVersions ));
+ for (size_t n = 0; n < m_pTable->size(); ++n)
+ {
+ SfxVersionInfo *pInfo = m_pTable->at( n );
+ OUString aEntry = formatDateTime(pInfo->aCreationDate, Application::GetSettings().GetLocaleDataWrapper(), false);
+ m_xVersionBox->append(weld::toId(pInfo), aEntry);
+ auto nLastRow = m_xVersionBox->n_children() - 1;
+ m_xVersionBox->set_text(nLastRow, pInfo->aAuthor, 1);
+ m_xVersionBox->set_text(nLastRow, ConvertWhiteSpaces_Impl(pInfo->aComment), 2);
+ }
+
+ if (auto nCount = m_pTable->size())
+ m_xVersionBox->select(nCount - 1);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */